libuav original

Dependents:   UAVCAN UAVCAN_Subscriber

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers storage_marshaller.hpp Source File

storage_marshaller.hpp

00001 /*
00002  * Copyright (C) 2015 Pavel Kirienko <pavel.kirienko@gmail.com>
00003  */
00004 
00005 #ifndef UAVCAN_PROTOCOL_DYNAMIC_NODE_ID_SERVER_STORAGE_MARSHALLER_HPP_INCLUDED
00006 #define UAVCAN_PROTOCOL_DYNAMIC_NODE_ID_SERVER_STORAGE_MARSHALLER_HPP_INCLUDED
00007 
00008 #include <uavcan/build_config.hpp>
00009 #include <uavcan/debug.hpp>
00010 #include <uavcan/protocol/dynamic_node_id_server/storage_backend.hpp>
00011 #include <uavcan/protocol/dynamic_node_id_server/types.hpp>
00012 #include <cstdlib>
00013 
00014 #if UAVCAN_CPP_VERSION >= UAVCAN_CPP11
00015 # include <cerrno>
00016 #endif
00017 
00018 namespace uavcan
00019 {
00020 namespace dynamic_node_id_server
00021 {
00022 /**
00023  * This class extends the storage backend interface with serialization/deserialization functionality.
00024  */
00025 class StorageMarshaller
00026 {
00027     IStorageBackend& storage_;
00028 
00029     static uint8_t convertLowerCaseHexCharToNibble(char ch)
00030     {
00031         const uint8_t ret = (ch > '9') ? static_cast<uint8_t>(ch - 'a' + 10) : static_cast<uint8_t>(ch - '0');
00032         UAVCAN_ASSERT(ret < 16);
00033         return ret;
00034     }
00035 
00036 public:
00037     StorageMarshaller(IStorageBackend& storage)
00038         : storage_(storage)
00039     { }
00040 
00041     /**
00042      * Turns a unique ID into a hex string that can be used as a key or as a value.
00043      */
00044     static IStorageBackend::String convertUniqueIDToHex(const UniqueID& key)
00045     {
00046         IStorageBackend::String serialized;
00047         for (uint8_t i = 0; i < UniqueID::MaxSize; i++)
00048         {
00049             serialized.appendFormatted("%02x", key.at(i));
00050         }
00051         UAVCAN_ASSERT(serialized.size() == UniqueID::MaxSize * 2);
00052         return serialized;
00053     }
00054 
00055     /**
00056      * These methods set the value and then immediately read it back.
00057      *  1. Serialize the value.
00058      *  2. Update the value on the backend.
00059      *  3. Call get() with the same value argument.
00060      * The caller then is supposed to check whether the argument has the desired value.
00061      */
00062     int setAndGetBack(const IStorageBackend::String& key, uint32_t& inout_value)
00063     {
00064         IStorageBackend::String serialized;
00065         serialized.appendFormatted("%llu", static_cast<unsigned long long>(inout_value));
00066 
00067         UAVCAN_TRACE("StorageMarshaller", "Set %s = %s", key.c_str(), serialized.c_str());
00068         storage_.set(key, serialized);
00069 
00070         return get(key, inout_value);
00071     }
00072 
00073     int setAndGetBack(const IStorageBackend::String& key, UniqueID& inout_value)
00074     {
00075         const IStorageBackend::String serialized = convertUniqueIDToHex(inout_value);
00076 
00077         UAVCAN_TRACE("StorageMarshaller", "Set %s = %s", key.c_str(), serialized.c_str());
00078         storage_.set(key, serialized);
00079 
00080         return get(key, inout_value);
00081     }
00082 
00083     /**
00084      * Getters simply read and deserialize the value.
00085      *  1. Read the value back from the backend; return false if read fails.
00086      *  2. Deserealize the newly read value; return false if deserialization fails.
00087      *  3. Update the argument with deserialized value.
00088      *  4. Return true.
00089      */
00090     int get(const IStorageBackend::String& key, uint32_t& out_value) const
00091     {
00092         /*
00093          * Reading the storage
00094          */
00095         const IStorageBackend::String val = storage_.get(key);
00096         if (val.empty())
00097         {
00098             return -ErrFailure;
00099         }
00100 
00101         /*
00102          * Per MISRA C++ recommendations, checking the inputs instead of relying solely on errno.
00103          * The value must contain only numeric characters.
00104          */
00105         for (IStorageBackend::String::const_iterator it = val.begin(); it != val.end(); ++it)
00106         {
00107             if (static_cast<char>(*it) < '0' || static_cast<char>(*it) > '9')
00108             {
00109                 return -ErrFailure;
00110             }
00111         }
00112 
00113         if (val.size() > 10) // len(str(0xFFFFFFFF))
00114         {
00115             return -ErrFailure;
00116         }
00117 
00118         /*
00119          * Conversion is carried out here
00120          */
00121 #if UAVCAN_CPP_VERSION >= UAVCAN_CPP11
00122         errno = 0;
00123 #endif
00124 
00125 #if UAVCAN_CPP_VERSION >= UAVCAN_CPP11
00126         const unsigned long long x = std::strtoull(val.c_str(), UAVCAN_NULLPTR, 10);
00127 #else
00128         // There was no strtoull() before C++11, so we need to resort to strtoul()
00129         StaticAssert<(sizeof(unsigned long) >= sizeof(uint32_t))>::check();
00130         const unsigned long x = std::strtoul(val.c_str(), UAVCAN_NULLPTR, 10);
00131 #endif
00132 
00133 #if UAVCAN_CPP_VERSION >= UAVCAN_CPP11
00134         if (errno != 0)
00135         {
00136             return -ErrFailure;
00137         }
00138 #endif
00139 
00140         out_value = static_cast<uint32_t>(x);
00141         return 0;
00142     }
00143 
00144     int get(const IStorageBackend::String& key, UniqueID& out_value) const
00145     {
00146         static const uint8_t NumBytes = UniqueID::MaxSize;
00147 
00148         /*
00149          * Reading the storage
00150          */
00151         IStorageBackend::String val = storage_.get(key);
00152         if (val.size() != NumBytes * 2)
00153         {
00154             return -ErrFailure;
00155         }
00156 
00157         /*
00158          * The value must contain only hexadecimal numbers.
00159          */
00160         val.convertToLowerCaseASCII();
00161         for (IStorageBackend::String::const_iterator it = val.begin(); it != val.end(); ++it)
00162         {
00163             if ((static_cast<char>(*it) < '0' || static_cast<char>(*it) > '9') &&
00164                 (static_cast<char>(*it) < 'a' || static_cast<char>(*it) > 'f'))
00165             {
00166                 return -ErrFailure;
00167             }
00168         }
00169 
00170         /*
00171          * Conversion is carried out here
00172          */
00173         IStorageBackend::String::const_iterator it = val.begin();
00174 
00175         for (uint8_t byte_index = 0; byte_index < NumBytes; byte_index++)
00176         {
00177             out_value[byte_index] =
00178                 static_cast<uint8_t>(convertLowerCaseHexCharToNibble(static_cast<char>(*it++)) << 4);
00179             out_value[byte_index] =
00180                 static_cast<uint8_t>(convertLowerCaseHexCharToNibble(static_cast<char>(*it++)) | out_value[byte_index]);
00181         }
00182 
00183         return 0;
00184     }
00185 };
00186 
00187 }
00188 }
00189 
00190 #endif // Include guard