From 5633cb8bdb813312ca804635832a7563009c29d5 Mon Sep 17 00:00:00 2001 From: Pavel Kirienko Date: Sun, 3 May 2015 12:28:26 +0300 Subject: [PATCH] Raft: Basic log methods --- .../dynamic_node_id_allocation_server.hpp | 33 ++-- .../uc_dynamic_node_id_allocation_server.cpp | 157 ++++++++++++++++-- .../dynamic_node_id_allocation_server.cpp | 22 +-- 3 files changed, 175 insertions(+), 37 deletions(-) diff --git a/libuavcan/include/uavcan/protocol/dynamic_node_id_allocation_server.hpp b/libuavcan/include/uavcan/protocol/dynamic_node_id_allocation_server.hpp index c310fb46bf..f3ea8eb833 100644 --- a/libuavcan/include/uavcan/protocol/dynamic_node_id_allocation_server.hpp +++ b/libuavcan/include/uavcan/protocol/dynamic_node_id_allocation_server.hpp @@ -31,7 +31,7 @@ namespace uavcan * The storage is represented as a key-value container, where keys and values are ASCII strings up to 32 * characters long, not including the termination byte. Fixed block size allows for absolutely straightforward * and efficient implementation of storage backends, e.g. based on text files. - * Keys and values may contain only alphanumeric characters and underscores. + * Keys and values may contain only non-whitespace, non-formatting printable characters. */ class IDynamicNodeIDStorageBackend { @@ -104,9 +104,9 @@ public: * 3. Call get() with the same value argument. * The caller then is supposed to check whether the argument has the desired value. */ - bool setAndGetBack(const IDynamicNodeIDStorageBackend::String& key, uint32_t& inout_value); - bool setAndGetBack(const IDynamicNodeIDStorageBackend::String& key, - protocol::dynamic_node_id::server::Entry::FieldTypes::unique_id& inout_value); + int setAndGetBack(const IDynamicNodeIDStorageBackend::String& key, uint32_t& inout_value); + int setAndGetBack(const IDynamicNodeIDStorageBackend::String& key, + protocol::dynamic_node_id::server::Entry::FieldTypes::unique_id& inout_value); /** * Getters simply read and deserialize the value. @@ -115,9 +115,9 @@ public: * 3. Update the argument with deserialized value. * 4. Return true. */ - bool get(const IDynamicNodeIDStorageBackend::String& key, uint32_t& out_value) const; - bool get(const IDynamicNodeIDStorageBackend::String& key, - protocol::dynamic_node_id::server::Entry::FieldTypes::unique_id& out_value) const; + int get(const IDynamicNodeIDStorageBackend::String& key, uint32_t& out_value) const; + int get(const IDynamicNodeIDStorageBackend::String& key, + protocol::dynamic_node_id::server::Entry::FieldTypes::unique_id& out_value) const; }; /** @@ -135,12 +135,17 @@ private: IDynamicNodeIDStorageBackend& storage_; protocol::dynamic_node_id::server::Entry entries_[Capacity]; - Index max_index_; // Index zero always contains an empty entry + Index last_index_; // Index zero always contains an empty entry + + static IDynamicNodeIDStorageBackend::String makeEntryKey(Index index, const char* postfix); + + int readEntryFromStorage(Index index, protocol::dynamic_node_id::server::Entry& out_entry); + int writeEntryToStorage(Index index, const protocol::dynamic_node_id::server::Entry& entry); public: Log(IDynamicNodeIDStorageBackend& storage) : storage_(storage) - , max_index_(0) + , last_index_(0) { } /** @@ -150,13 +155,15 @@ public: /** * This method invokes storage IO. + * Returned value indicates whether the entry was successfully appended. */ - void append(const protocol::dynamic_node_id::server::Entry& entry); + int append(const protocol::dynamic_node_id::server::Entry& entry); /** * This method invokes storage IO. + * Returned value indicates whether the requested operation has been carried out successfully. */ - void removeEntriesWhereIndexGreaterOrEqual(Index index); + int removeEntriesWhereIndexGreaterOrEqual(Index index); /** * Returns nullptr if there's no such index. @@ -164,7 +171,7 @@ public: */ const protocol::dynamic_node_id::server::Entry* getEntryAtIndex(Index index) const; - Index getMaxIndex() const { return max_index_; } + Index getLastIndex() const { return last_index_; } bool isOtherLogUpToDate(Index other_last_index, Term other_last_term) const; }; @@ -412,7 +419,7 @@ public: inline LazyConstructor traverseLogFromEndUntil(const Predicate& predicate) const { UAVCAN_ASSERT(try_implicit_cast(predicate, true)); - for (int index = static_cast(persistent_state_.getLog().getMaxIndex()); index--; index >= 0) + for (int index = static_cast(persistent_state_.getLog().getLastIndex()); index--; index >= 0) { const protocol::dynamic_node_id::server::Entry* const entry = persistent_state_.getLog().getEntryAtIndex(Log::Index(index)); diff --git a/libuavcan/src/protocol/uc_dynamic_node_id_allocation_server.cpp b/libuavcan/src/protocol/uc_dynamic_node_id_allocation_server.cpp index 0dc68bbe06..a611bd5b6a 100644 --- a/libuavcan/src/protocol/uc_dynamic_node_id_allocation_server.cpp +++ b/libuavcan/src/protocol/uc_dynamic_node_id_allocation_server.cpp @@ -27,7 +27,7 @@ uint8_t MarshallingStorageDecorator::convertLowerCaseHexCharToNibble(char ch) return ret; } -bool MarshallingStorageDecorator::setAndGetBack(const IDynamicNodeIDStorageBackend::String& key, uint32_t& inout_value) +int MarshallingStorageDecorator::setAndGetBack(const IDynamicNodeIDStorageBackend::String& key, uint32_t& inout_value) { IDynamicNodeIDStorageBackend::String serialized; serialized.appendFormatted("%llu", static_cast(inout_value)); @@ -38,7 +38,7 @@ bool MarshallingStorageDecorator::setAndGetBack(const IDynamicNodeIDStorageBacke return get(key, inout_value); } -bool MarshallingStorageDecorator::setAndGetBack(const IDynamicNodeIDStorageBackend::String& key, +int MarshallingStorageDecorator::setAndGetBack(const IDynamicNodeIDStorageBackend::String& key, protocol::dynamic_node_id::server::Entry::FieldTypes::unique_id& inout_value) { IDynamicNodeIDStorageBackend::String serialized; @@ -54,7 +54,7 @@ bool MarshallingStorageDecorator::setAndGetBack(const IDynamicNodeIDStorageBacke return get(key, inout_value); } -bool MarshallingStorageDecorator::get(const IDynamicNodeIDStorageBackend::String& key, uint32_t& out_value) const +int MarshallingStorageDecorator::get(const IDynamicNodeIDStorageBackend::String& key, uint32_t& out_value) const { /* * Reading the storage @@ -62,7 +62,7 @@ bool MarshallingStorageDecorator::get(const IDynamicNodeIDStorageBackend::String const IDynamicNodeIDStorageBackend::String val = storage_.get(key); if (val.empty()) { - return false; + return -ErrFailure; } /* @@ -73,13 +73,13 @@ bool MarshallingStorageDecorator::get(const IDynamicNodeIDStorageBackend::String { if (static_cast(*it) < '0' || static_cast(*it) > '9') { - return false; + return -ErrFailure; } } if (val.size() > 10) // len(str(0xFFFFFFFF)) { - return false; + return -ErrFailure; } /* @@ -100,16 +100,16 @@ bool MarshallingStorageDecorator::get(const IDynamicNodeIDStorageBackend::String #if UAVCAN_CPP_VERSION >= UAVCAN_CPP11 if (errno != 0) { - return false; + return -ErrFailure; } #endif out_value = static_cast(x); - return true; + return 0; } -bool MarshallingStorageDecorator::get(const IDynamicNodeIDStorageBackend::String& key, - protocol::dynamic_node_id::server::Entry::FieldTypes::unique_id& out_value) const +int MarshallingStorageDecorator::get(const IDynamicNodeIDStorageBackend::String& key, + protocol::dynamic_node_id::server::Entry::FieldTypes::unique_id& out_value) const { static const uint8_t NumBytes = protocol::dynamic_node_id::server::Entry::FieldTypes::unique_id::MaxSize; @@ -119,7 +119,7 @@ bool MarshallingStorageDecorator::get(const IDynamicNodeIDStorageBackend::String IDynamicNodeIDStorageBackend::String val = storage_.get(key); if (val.size() != NumBytes * 2) { - return false; + return -ErrFailure; } /* @@ -132,7 +132,7 @@ bool MarshallingStorageDecorator::get(const IDynamicNodeIDStorageBackend::String if ((static_cast(*it) < '0' || static_cast(*it) > '9') && (static_cast(*it) < 'a' || static_cast(*it) > 'f')) { - return false; + return -ErrFailure; } } @@ -148,7 +148,138 @@ bool MarshallingStorageDecorator::get(const IDynamicNodeIDStorageBackend::String out_value[byte_index]); } - return true; + return 0; +} + +/* + * Log + */ +IDynamicNodeIDStorageBackend::String Log::makeEntryKey(Index index, const char* postfix) +{ + IDynamicNodeIDStorageBackend::String str; + // "log0.foobar" + str += "log"; + str.appendFormatted("%d", int(index)); + str += "."; + str += postfix; + return str; +} + +int Log::readEntryFromStorage(Index index, protocol::dynamic_node_id::server::Entry& out_entry) +{ + const MarshallingStorageDecorator io(storage_); + + // Term + if (io.get(makeEntryKey(index, "term"), out_entry.term) < 0) + { + return -ErrFailure; + } + + // Unique ID + if (io.get(makeEntryKey(index, "unique_id"), out_entry.unique_id) < 0) + { + return -ErrFailure; + } + + // Node ID + uint32_t node_id = 0; + if (io.get(makeEntryKey(index, "node_id"), node_id) < 0) + { + return -ErrFailure; + } + out_entry.node_id = static_cast(node_id); + + return 0; +} + +int Log::writeEntryToStorage(Index index, const protocol::dynamic_node_id::server::Entry& entry) +{ + protocol::dynamic_node_id::server::Entry temp = entry; + + MarshallingStorageDecorator io(storage_); + + // Term + if (io.setAndGetBack(makeEntryKey(index, "term"), temp.term) < 0) + { + return -ErrFailure; + } + + // Unique ID + if (io.setAndGetBack(makeEntryKey(index, "unique_id"), temp.unique_id) < 0) + { + return -ErrFailure; + } + + // Node ID + uint32_t node_id = entry.node_id; + if (io.setAndGetBack(makeEntryKey(index, "node_id"), node_id) < 0) + { + return -ErrFailure; + } + temp.node_id = static_cast(node_id); + + return (temp == entry) ? 0 : -ErrFailure; +} + +int Log::init() +{ + const MarshallingStorageDecorator io(storage_); + + // Reading max index + { + IDynamicNodeIDStorageBackend::String key; + key = "log_last_index"; + uint32_t value = 0; + if (io.get(key, value) < 0) + { + return -ErrFailure; + } + if (value >= Capacity) + { + return -ErrFailure; + } + last_index_ = Index(value); + } + + // Restoring log entries + for (Index index = 0; index <= last_index_; index++) + { + const int result = readEntryFromStorage(index, entries_[index]); + if (result < 0) + { + return result; + } + } + + return 0; +} + +int Log::append(const protocol::dynamic_node_id::server::Entry& entry) +{ + (void)entry; + return -1; +} + +int Log::removeEntriesWhereIndexGreaterOrEqual(Index index) +{ + (void)index; + return -1; +} + +const protocol::dynamic_node_id::server::Entry* Log::getEntryAtIndex(Index index) const +{ + return (index <= last_index_) ? &entries_[index] : NULL; +} + +bool Log::isOtherLogUpToDate(Log::Index other_last_index, Term other_last_term) const +{ + // Terms are different - the one with higher term is more up-to-date + if (other_last_term != entries_[last_index_].term) + { + return other_last_term > entries_[last_index_].term; + } + // Terms are equal - longer log wins + return other_last_index >= last_index_; } } // dynamic_node_id_server_impl diff --git a/libuavcan/test/protocol/dynamic_node_id_allocation_server.cpp b/libuavcan/test/protocol/dynamic_node_id_allocation_server.cpp index 39af887c1a..e509bbba7d 100644 --- a/libuavcan/test/protocol/dynamic_node_id_allocation_server.cpp +++ b/libuavcan/test/protocol/dynamic_node_id_allocation_server.cpp @@ -52,22 +52,22 @@ TEST(DynamicNodeIDAllocationServer, MarshallingStorageDecorator) key = "foo"; u32 = 0; - ASSERT_TRUE(marshaler.setAndGetBack(key, u32)); + ASSERT_LE(0, marshaler.setAndGetBack(key, u32)); ASSERT_EQ(0, u32); key = "bar"; u32 = 0xFFFFFFFF; - ASSERT_TRUE(marshaler.setAndGetBack(key, u32)); + ASSERT_LE(0, marshaler.setAndGetBack(key, u32)); ASSERT_EQ(0xFFFFFFFF, u32); - ASSERT_TRUE(marshaler.get(key, u32)); + ASSERT_LE(0, marshaler.get(key, u32)); ASSERT_EQ(0xFFFFFFFF, u32); key = "foo"; - ASSERT_TRUE(marshaler.get(key, u32)); + ASSERT_LE(0, marshaler.get(key, u32)); ASSERT_EQ(0, u32); key = "the_cake_is_a_lie"; - ASSERT_FALSE(marshaler.get(key, u32)); + ASSERT_GT(0, marshaler.get(key, u32)); ASSERT_EQ(0, u32); /* @@ -77,14 +77,14 @@ TEST(DynamicNodeIDAllocationServer, MarshallingStorageDecorator) key = "a"; // Set zero - ASSERT_TRUE(marshaler.setAndGetBack(key, array)); + ASSERT_LE(0, marshaler.setAndGetBack(key, array)); for (uint8_t i = 0; i < 16; i++) { ASSERT_EQ(0, array[i]); } // Make sure this will not be interpreted as uint32 - ASSERT_FALSE(marshaler.get(key, u32)); + ASSERT_GT(0, marshaler.get(key, u32)); ASSERT_EQ(0, u32); // Set pattern @@ -92,7 +92,7 @@ TEST(DynamicNodeIDAllocationServer, MarshallingStorageDecorator) { array[i] = uint8_t(i + 1); } - ASSERT_TRUE(marshaler.setAndGetBack(key, array)); + ASSERT_LE(0, marshaler.setAndGetBack(key, array)); for (uint8_t i = 0; i < 16; i++) { ASSERT_EQ(i + 1, array[i]); @@ -103,7 +103,7 @@ TEST(DynamicNodeIDAllocationServer, MarshallingStorageDecorator) { array[i] = uint8_t(i | (i << 4)); } - ASSERT_TRUE(marshaler.setAndGetBack(key, array)); + ASSERT_LE(0, marshaler.setAndGetBack(key, array)); for (uint8_t i = 0; i < 16; i++) { ASSERT_EQ(uint8_t(i | (i << 4)), array[i]); @@ -111,9 +111,9 @@ TEST(DynamicNodeIDAllocationServer, MarshallingStorageDecorator) // Make sure uint32 cannot be interpreted as an array key = "foo"; - ASSERT_FALSE(marshaler.get(key, array)); + ASSERT_GT(0, marshaler.get(key, array)); // Nonexistent key key = "the_cake_is_a_lie"; - ASSERT_FALSE(marshaler.get(key, array)); + ASSERT_GT(0, marshaler.get(key, array)); }