libuav original
Dependents: UAVCAN UAVCAN_Subscriber
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
Generated on Tue Jul 12 2022 17:17:34 by 1.7.2