diff --git a/routing/maxspeed_conversion.cpp b/routing/maxspeed_conversion.cpp index f2c697c8b2..441561a0d3 100644 --- a/routing/maxspeed_conversion.cpp +++ b/routing/maxspeed_conversion.cpp @@ -9,16 +9,16 @@ namespace routing using namespace measurement_utils; // SpeedInUnits ------------------------------------------------------------------------------------ -bool SpeedInUnits::operator==(SpeedInUnits const & s) const +bool SpeedInUnits::operator==(SpeedInUnits const & rhs) const { - return m_speed == s.m_speed && m_units == s.m_units; + return m_speed == rhs.m_speed && m_units == rhs.m_units; } -bool SpeedInUnits::operator<(SpeedInUnits const & s) const +bool SpeedInUnits::operator<(SpeedInUnits const & rhs) const { - if (m_speed != s.m_speed) - return m_speed < s.m_speed; - return m_units < s.m_units; + if (m_speed != rhs.m_speed) + return m_speed < rhs.m_speed; + return m_units < rhs.m_units; } bool SpeedInUnits::IsNumeric() const @@ -26,6 +26,18 @@ bool SpeedInUnits::IsNumeric() const return m_speed != kNoneMaxSpeed && m_speed != kWalkMaxSpeed && m_speed != kInvalidSpeed; } +// FeatureMaxspeed --------------------------------------------------------------------------------- +FeatureMaxspeed::FeatureMaxspeed(uint32_t fid, SpeedInUnits const & forward, + SpeedInUnits const & backward /* = SpeedInUnits() */) + : m_featureId(fid), m_forward(forward), m_backward(backward) +{ +} + +bool FeatureMaxspeed::operator==(FeatureMaxspeed const & rhs) const +{ + return m_featureId == rhs.m_featureId && m_forward == rhs.m_forward && m_backward == rhs.m_backward; +} + // MaxspeedConverter ------------------------------------------------------------------------------- MaxspeedConverter::MaxspeedConverter() { @@ -205,6 +217,14 @@ MaxspeedConverter const & GetMaxspeedConverter() return inst; } +std::string DebugPrint(Maxspeed maxspeed) +{ + std::ostringstream oss; + oss << "Maxspeed:" << static_cast(maxspeed) << " Decoded:" + << DebugPrint(GetMaxspeedConverter().MacroToSpeed(maxspeed)); + return oss.str(); +} + std::string DebugPrint(SpeedInUnits const & speed) { std::ostringstream oss; @@ -213,11 +233,12 @@ std::string DebugPrint(SpeedInUnits const & speed) return oss.str(); } -std::string DebugPrint(Maxspeed maxspeed) +std::string DebugPrint(FeatureMaxspeed const & featureMaxspeed) { std::ostringstream oss; - oss << "Maxspeed:" << static_cast(maxspeed) << " Decoded:" - << DebugPrint(GetMaxspeedConverter().MacroToSpeed(maxspeed)); + oss << "FeatureMaxspeed [ m_featureId:" << featureMaxspeed.GetFeatureId() + << " m_forward:" << DebugPrint(featureMaxspeed.GetForward()) + << " m_backward:" << DebugPrint(featureMaxspeed.GetBackward()) << " ]"; return oss.str(); } } // namespace routing diff --git a/routing/maxspeed_conversion.hpp b/routing/maxspeed_conversion.hpp index 55865dfd35..638dc6d2aa 100644 --- a/routing/maxspeed_conversion.hpp +++ b/routing/maxspeed_conversion.hpp @@ -165,10 +165,10 @@ struct SpeedInUnits SpeedInUnits() = default; SpeedInUnits(uint16_t speed, measurement_utils::Units units) noexcept : m_speed(speed), m_units(units) {} - bool operator==(SpeedInUnits const & s) const; + bool operator==(SpeedInUnits const & rhs) const; /// \note While comparing speeds are not converted to the same units. So according to /// this compare operator 80 km per hour is more then 79 mile per hour. - bool operator<(SpeedInUnits const & s) const; + bool operator<(SpeedInUnits const & rhs) const; bool IsNumeric() const; bool IsValid() const { return m_speed != kInvalidSpeed; } @@ -177,6 +177,33 @@ struct SpeedInUnits measurement_utils::Units m_units = measurement_utils::Units::Metric; }; +/// \breif Maxspeed tag value for feature id. |m_forward| and |m_backward| fields reflect the fact +/// that a feature may have different maxspeed tag value for different directions. +/// If |m_backward| is invalid it means that |m_forward| tag contains maxspeed for the both +/// directions. +class FeatureMaxspeed +{ +public: + FeatureMaxspeed(uint32_t fid, SpeedInUnits const & forward, + SpeedInUnits const & backward = SpeedInUnits()); + + /// \note operator==() and operator<() do not correspond to each other. + bool operator==(FeatureMaxspeed const & rhs) const; + bool operator<(FeatureMaxspeed const & rhs) const { return m_featureId < rhs.m_featureId; } + + bool IsValid() const { return m_forward.IsValid(); } + bool IsBidirectional() const { return IsValid() && m_backward.IsValid(); } + + uint32_t GetFeatureId() const { return m_featureId; } + SpeedInUnits const & GetForward() const { return m_forward; } + SpeedInUnits const & GetBackward() const { return m_backward; } + +private: + uint32_t m_featureId = 0; + SpeedInUnits m_forward; + SpeedInUnits m_backward; +}; + class MaxspeedConverter { friend MaxspeedConverter const & GetMaxspeedConverter(); @@ -198,6 +225,7 @@ private: MaxspeedConverter const & GetMaxspeedConverter(); -std::string DebugPrint(SpeedInUnits const & speed); std::string DebugPrint(Maxspeed maxspeed); +std::string DebugPrint(SpeedInUnits const & speed); +std::string DebugPrint(FeatureMaxspeed const & featureMaxspeed); } // namespace routing diff --git a/routing/maxspeed_serialization.hpp b/routing/maxspeed_serialization.hpp index ad0b25da77..fb9a67b807 100644 --- a/routing/maxspeed_serialization.hpp +++ b/routing/maxspeed_serialization.hpp @@ -1,5 +1,97 @@ #pragma once +#include "routing/maxspeed_conversion.hpp" + +#include "coding/reader.hpp" +#include "coding/write_to_sink.hpp" + +#include "base/assert.hpp" + +#include +#include +#include + namespace routing { +class MaxspeedSerializer +{ +public: + MaxspeedSerializer() = delete; + + template + static void Serialize(std::vector const & speeds, Sink & sink) + { + CHECK(std::is_sorted(speeds.cbegin(), speeds.cend()), ()); + + // @TODO(bykoianko) Now serialization is implemented in the simplest way for research purposes. + // It should be rewrite in a better way using MaxspeedConverter before before the PR is merged. + Header header(static_cast(speeds.size())); + header.Serialize(sink); + + for (auto const & s : speeds) + { + WriteToSink(sink, s.GetFeatureId()); + WriteToSink(sink, s.GetForward().m_speed); + WriteToSink(sink, static_cast(s.GetForward().m_units)); + WriteToSink(sink, s.GetBackward().m_speed); + WriteToSink(sink, static_cast(s.GetBackward().m_units)); + } + } + + template + static void Deserialize(Source & src, std::vector & speeds) + { + Header header; + header.Deserialize(src); + + speeds.clear(); + for (size_t i = 0; i < header.GetSize(); ++i) + { + auto const fid = ReadPrimitiveFromSource(src); + + SpeedInUnits forward; + forward.m_speed = ReadPrimitiveFromSource(src); + forward.m_units = static_cast(ReadPrimitiveFromSource(src)); + + SpeedInUnits backward; + backward.m_speed = ReadPrimitiveFromSource(src); + backward.m_units = static_cast(ReadPrimitiveFromSource(src)); + + speeds.emplace_back(fid, forward, backward); + } + } + +private: + static uint8_t constexpr kLastVersion = 0; + + class Header final + { + public: + Header() = default; + explicit Header(uint32_t size) : m_size(size) {} + + template + void Serialize(Sink & sink) const + { + WriteToSink(sink, m_version); + WriteToSink(sink, m_reserved); + WriteToSink(sink, m_size); + } + + template + void Deserialize(Source & src) + { + m_version = ReadPrimitiveFromSource(src); + m_reserved = ReadPrimitiveFromSource(src); + m_size = ReadPrimitiveFromSource(src); + } + + uint32_t GetSize() const { return m_size; } + + private: + uint8_t m_version = kLastVersion; + uint8_t m_reserved = 0; + uint32_t m_size = 0; + }; +}; } // namespace routing diff --git a/routing/routing_tests/maxspeed_tests.cpp b/routing/routing_tests/maxspeed_tests.cpp index ba869d02a1..86384a8d99 100644 --- a/routing/routing_tests/maxspeed_tests.cpp +++ b/routing/routing_tests/maxspeed_tests.cpp @@ -1,11 +1,16 @@ #include "testing/testing.hpp" #include "routing/maxspeed_conversion.hpp" +#include "routing/maxspeed_serialization.hpp" + +#include "coding/reader.hpp" +#include "coding/writer.hpp" #include "platform/measurement_utils.hpp" #include #include +#include namespace { @@ -13,6 +18,23 @@ using namespace measurement_utils; using namespace routing; using namespace std; +void TestMaxspeedSerialization(std::vector const & speed) +{ + vector buffer; + MemWriter > w(buffer); + + MaxspeedSerializer::Serialize(speed, w); + + size_t const sz = buffer.size(); + + MemReader r(buffer.data(), sz); + ReaderSource src(r); + std::vector result; + MaxspeedSerializer::Deserialize(src, result); + + TEST_EQUAL(speed, result, ()); +} + UNIT_TEST(MaxspeedConverter) { auto const & conv = GetMaxspeedConverter(); @@ -51,4 +73,87 @@ UNIT_TEST(MaxspeedConverter) TEST(conv.IsValidMacro(1), ()); // static_cast(None) == 1 TEST(!conv.IsValidMacro(9), ()); // A value which is undefined in Maxspeed enum class. } + +UNIT_TEST(MaxspeedSerializer_Smoke) +{ + TestMaxspeedSerialization({}); +} + +UNIT_TEST(MaxspeedSerializer_OneForwardMetric) +{ + TestMaxspeedSerialization( + {FeatureMaxspeed(0 /* feature id */, SpeedInUnits(20 /* speed */, Units::Metric))}); +} + +UNIT_TEST(MaxspeedSerializer_OneNone) +{ + TestMaxspeedSerialization( + {FeatureMaxspeed(0 /* feature id */, SpeedInUnits(kNoneMaxSpeed, Units::Metric))}); +} + +UNIT_TEST(MaxspeedSerializer_OneWalk) +{ + TestMaxspeedSerialization( + {FeatureMaxspeed(0 /* feature id */, SpeedInUnits(kWalkMaxSpeed, Units::Metric))}); +} + +UNIT_TEST(MaxspeedSerializer_OneBidirectionalMetric_1) +{ + TestMaxspeedSerialization( + {FeatureMaxspeed(0 /* feature id */, SpeedInUnits(20 /* speed */, Units::Metric), + SpeedInUnits(40 /* speed */, Units::Metric))}); +} + +UNIT_TEST(MaxspeedSerializer_OneBidirectionalMetric_2) +{ + TestMaxspeedSerialization( + {FeatureMaxspeed(0 /* feature id */, SpeedInUnits(10 /* speed */, Units::Metric), + SpeedInUnits(kWalkMaxSpeed, Units::Metric))}); +} + +UNIT_TEST(MaxspeedSerializer_OneBidirectionalImperial) +{ + TestMaxspeedSerialization( + {FeatureMaxspeed(0 /* feature id */, SpeedInUnits(30 /* speed */, Units::Imperial), + SpeedInUnits(50 /* speed */, Units::Imperial))}); +} + +UNIT_TEST(MaxspeedSerializer_BigMetric) +{ + std::vector const maxspeeds = { + FeatureMaxspeed(0 /* feature id */, SpeedInUnits(20 /* speed */, Units::Metric)), + FeatureMaxspeed(1 /* feature id */, SpeedInUnits(60 /* speed */, Units::Metric)), + FeatureMaxspeed(4 /* feature id */, SpeedInUnits(90 /* speed */, Units::Metric)), + FeatureMaxspeed(5 /* feature id */, SpeedInUnits(5 /* speed */, Units::Metric)), + FeatureMaxspeed(7 /* feature id */, SpeedInUnits(70 /* speed */, Units::Metric), + SpeedInUnits(90 /* speed */, Units::Metric)), + FeatureMaxspeed(8 /* feature id */, SpeedInUnits(100 /* speed */, Units::Metric)), + FeatureMaxspeed(9 /* feature id */, SpeedInUnits(60 /* speed */, Units::Metric)), + FeatureMaxspeed(10 /* feature id */, SpeedInUnits(kNoneMaxSpeed, Units::Metric)), + FeatureMaxspeed(11 /* feature id */, SpeedInUnits(40 /* speed */, Units::Metric), + SpeedInUnits(50 /* speed */, Units::Metric)), + FeatureMaxspeed(12 /* feature id */, SpeedInUnits(40 /* speed */, Units::Metric), + SpeedInUnits(60 /* speed */, Units::Metric)), + }; + TestMaxspeedSerialization(maxspeeds); +} + +UNIT_TEST(MaxspeedSerializer_BigImperial) +{ + std::vector const maxspeeds = { + FeatureMaxspeed(0 /* feature id */, SpeedInUnits(30 /* speed */, Units::Imperial)), + FeatureMaxspeed(1 /* feature id */, SpeedInUnits(5 /* speed */, Units::Imperial)), + FeatureMaxspeed(4 /* feature id */, SpeedInUnits(1 /* speed */, Units::Imperial)), + FeatureMaxspeed(5 /* feature id */, SpeedInUnits(5 /* speed */, Units::Imperial)), + FeatureMaxspeed(7 /* feature id */, SpeedInUnits(30 /* speed */, Units::Imperial), + SpeedInUnits(50 /* speed */, Units::Imperial)), + FeatureMaxspeed(8 /* feature id */, SpeedInUnits(70 /* speed */, Units::Imperial)), + FeatureMaxspeed(9 /* feature id */, SpeedInUnits(50 /* speed */, Units::Imperial)), + FeatureMaxspeed(10 /* feature id */, SpeedInUnits(40 /* speed */, Units::Imperial), + SpeedInUnits(50 /* speed */, Units::Metric)), + FeatureMaxspeed(11 /* feature id */, SpeedInUnits(30 /* speed */, Units::Imperial), + SpeedInUnits(50 /* speed */, Units::Metric)), + }; + TestMaxspeedSerialization(maxspeeds); +} } // namespace