diff options
author | Arsentiy Milchakov <milcars@mapswithme.com> | 2017-07-28 13:35:46 +0300 |
---|---|---|
committer | Yuri Gorshenin <mipt.vi002@gmail.com> | 2017-07-31 17:09:17 +0300 |
commit | 84183a61840c34e10faf673638151fe8b3c6a2bc (patch) | |
tree | 59b920aadd3d10fe995f7a5a8a6dcd4c1f8f7ee9 | |
parent | 1c252b73166a2974637463f19b37d9bee3c72754 (diff) |
MAPSME-5094 localads serialization
-rw-r--r-- | local_ads/campaign.hpp | 33 | ||||
-rw-r--r-- | local_ads/campaign_serialization.cpp | 124 | ||||
-rw-r--r-- | local_ads/campaign_serialization.hpp | 14 | ||||
-rw-r--r-- | local_ads/local_ads_tests/campaign_serialization_test.cpp | 57 | ||||
-rw-r--r-- | local_ads/pylocal_ads/bindings.cpp | 5 | ||||
-rw-r--r-- | local_ads/pylocal_ads/bindings_test.py | 27 |
6 files changed, 206 insertions, 54 deletions
diff --git a/local_ads/campaign.hpp b/local_ads/campaign.hpp index 172e4f114f..5eb795ef7f 100644 --- a/local_ads/campaign.hpp +++ b/local_ads/campaign.hpp @@ -9,30 +9,35 @@ namespace local_ads { struct Campaign { - Campaign(uint32_t featureId, - uint16_t iconId, - uint8_t daysBeforeExpired, - bool priorityBit) + // Constructor for data Version::v1 + Campaign(uint32_t featureId, uint16_t iconId, uint8_t daysBeforeExpired) + : m_featureId(featureId), m_iconId(iconId), m_daysBeforeExpired(daysBeforeExpired) + { + } + + // Constructor for data Version::v2 + Campaign(uint32_t featureId, uint16_t iconId, uint8_t daysBeforeExpired, uint8_t zoomLevel, + uint8_t priority) : m_featureId(featureId) , m_iconId(iconId) , m_daysBeforeExpired(daysBeforeExpired) - , m_priorityBit(priorityBit) + , m_minZoomLevel(zoomLevel) + , m_priority(priority) { } std::string GetIconName() const { return IconsInfo::Instance().GetIcon(m_iconId); } - uint32_t m_featureId; - uint16_t m_iconId; - uint8_t m_daysBeforeExpired; - bool m_priorityBit; + uint32_t m_featureId = 0; + uint16_t m_iconId = 0; + uint8_t m_daysBeforeExpired = 0; + uint8_t m_minZoomLevel = 16; // supported values range: 10-17 + uint8_t m_priority = 0; // supported values range: 0-7 }; inline bool operator==(Campaign const & a, Campaign const & b) { - return - a.m_featureId == b.m_featureId && - a.m_iconId == b.m_iconId && - a.m_daysBeforeExpired == b.m_daysBeforeExpired && - a.m_priorityBit == b.m_priorityBit; + return a.m_featureId == b.m_featureId && a.m_iconId == b.m_iconId && + a.m_daysBeforeExpired == b.m_daysBeforeExpired && a.m_minZoomLevel == b.m_minZoomLevel && + a.m_priority == b.m_priority; } } // namespace local_ads diff --git a/local_ads/campaign_serialization.cpp b/local_ads/campaign_serialization.cpp index 2b43c093eb..4b64e1a87d 100644 --- a/local_ads/campaign_serialization.cpp +++ b/local_ads/campaign_serialization.cpp @@ -11,6 +11,15 @@ namespace { +using namespace local_ads; + +auto const kHalfByteShift = 0x4; +auto const kLowerMask = 0xF; +auto const kUpperMask = 0xF0; +auto const kMinZoomLevel = 10; +auto const kMaxZoomLevel = 17; +auto const kMaxPriority = 7; + template<typename T> constexpr bool IsEnumOrIntegral() { @@ -52,52 +61,123 @@ std::vector<Integral> ReadData(ByteStream & s, size_t chunksNumber) return result; } +std::vector<Campaign> DeserializeV1(std::vector<uint8_t> const & bytes) +{ + ArrayByteSource src(bytes.data()); + CHECK_EQUAL(Read<Version>(src), Version::v1, ()); + auto const chunksNumber = Read<uint64_t>(src); + + auto const featureIds = ReadData<uint32_t>(src, chunksNumber); + auto const icons = ReadData<uint16_t>(src, chunksNumber); + auto const expirations = ReadData<uint8_t>(src, chunksNumber); + + CHECK_EQUAL(featureIds.size(), chunksNumber, ()); + CHECK_EQUAL(icons.size(), chunksNumber, ()); + CHECK_EQUAL(expirations.size(), chunksNumber, ()); + + std::vector<Campaign> campaigns; + campaigns.reserve(chunksNumber); + for (size_t i = 0; i < chunksNumber; ++i) + { + campaigns.emplace_back(featureIds[i], icons[i], expirations[i]); + } + return campaigns; +} + +uint8_t ZoomIndex(uint8_t zoomValue) { return zoomValue - kMinZoomLevel; } + +uint8_t ZoomValue(uint8_t zoomIndex) { return zoomIndex + kMinZoomLevel; } + +std::vector<Campaign> DeserializeV2(std::vector<uint8_t> const & bytes) +{ + ArrayByteSource src(bytes.data()); + CHECK_EQUAL(Read<Version>(src), Version::v2, ()); + auto const chunksNumber = Read<uint64_t>(src); + + auto const featureIds = ReadData<uint32_t>(src, chunksNumber); + auto const icons = ReadData<uint16_t>(src, chunksNumber); + auto const expirations = ReadData<uint8_t>(src, chunksNumber); + auto const zoomAndPriority = ReadData<uint8_t>(src, chunksNumber); + + CHECK_EQUAL(featureIds.size(), chunksNumber, ()); + CHECK_EQUAL(icons.size(), chunksNumber, ()); + CHECK_EQUAL(expirations.size(), chunksNumber, ()); + CHECK_EQUAL(zoomAndPriority.size(), chunksNumber, ()); + + std::vector<Campaign> campaigns; + campaigns.reserve(chunksNumber); + for (size_t i = 0; i < chunksNumber; ++i) + { + campaigns.emplace_back(featureIds[i], icons[i], expirations[i], + ZoomValue(zoomAndPriority[i] & kLowerMask), + (zoomAndPriority[i] >> kHalfByteShift) & kLowerMask); + + ASSERT_GREATER_OR_EQUAL(campaigns.back().m_minZoomLevel, kMinZoomLevel, + ("Unsupported zoom level")); + ASSERT_LESS_OR_EQUAL(campaigns.back().m_minZoomLevel, kMaxZoomLevel, + ("Unsupported zoom level")); + ASSERT_LESS_OR_EQUAL(campaigns.back().m_priority, kMaxPriority, ("Unsupported priority value")); + } + return campaigns; +} } // namespace namespace local_ads { -std::vector<uint8_t> Serialize(std::vector<Campaign> const & campaigns) +std::vector<uint8_t> Serialize(std::vector<Campaign> const & campaigns, Version const version) { std::vector<uint8_t> buff; PushBackByteSink<decltype(buff)> dst(buff); - Write(dst, Version::latest); + Write(dst, version); Write(dst, campaigns.size()); for (auto const & c : campaigns) - WriteVarUint(dst, c.m_featureId); + WriteVarUint(dst, c.m_featureId); for (auto const & c : campaigns) WriteVarUint(dst, c.m_iconId); - for (auto const & c : campaigns) Write(dst, c.m_daysBeforeExpired); + for (auto const & c : campaigns) + { + ASSERT_GREATER_OR_EQUAL(c.m_minZoomLevel, kMinZoomLevel, ("Unsupported zoom level")); + ASSERT_LESS_OR_EQUAL(c.m_minZoomLevel, kMaxZoomLevel, ("Unsupported zoom level")); + ASSERT_LESS_OR_EQUAL(c.m_priority, kMaxPriority, ("Unsupported priority value")); + + Write(dst, static_cast<uint8_t>((ZoomIndex(c.m_minZoomLevel) & kLowerMask) | + ((c.m_priority << kHalfByteShift) & kUpperMask))); + } + return buff; } +std::vector<uint8_t> Serialize(std::vector<Campaign> const & campaigns) +{ + return Serialize(campaigns, Version::latest); +} + std::vector<Campaign> Deserialize(std::vector<uint8_t> const & bytes) { ArrayByteSource src(bytes.data()); auto const version = Read<Version>(src); - static_cast<void>(version); // No version dispatching for now. - auto const chunksNumber = Read<uint64_t>(src); - auto const featureIds = ReadData<uint32_t>(src, chunksNumber); - auto const icons = ReadData<uint16_t>(src, chunksNumber); - auto const expirations = ReadData<uint8_t>(src, chunksNumber); + switch (version) + { + case Version::v1: return DeserializeV1(bytes); + case Version::v2: return DeserializeV2(bytes); + default: ASSERT(false, ("Unknown version type")); + } - CHECK_EQUAL(featureIds.size(), chunksNumber, ()); - CHECK_EQUAL(icons.size(), chunksNumber, ()); - CHECK_EQUAL(expirations.size(), chunksNumber, ()); + return {}; +} - std::vector<Campaign> campaigns; - campaigns.reserve(chunksNumber); - for (size_t i = 0; i < chunksNumber; ++i) +std::string DebugPrint(local_ads::Version version) +{ + using local_ads::Version; + + switch (version) { - campaigns.emplace_back( - featureIds[i], - icons[i], - expirations[i], - true /* priorityBit */ - ); + case Version::unknown: return "Unknown"; + case Version::v1: return "Version 1"; + case Version::v2: return "Version 2"; } - return campaigns; } } // namespace local_ads diff --git a/local_ads/campaign_serialization.hpp b/local_ads/campaign_serialization.hpp index a73b78b6ed..167c0259a0 100644 --- a/local_ads/campaign_serialization.hpp +++ b/local_ads/campaign_serialization.hpp @@ -10,11 +10,17 @@ namespace local_ads enum class Version { unknown = -1, - v1 = 0, // March 2017 (Store feature ids and icon ids as varints, - // use one byte for days before expiration.) - latest = v1 -}; + // March 2017 (store feature ids and icon ids as varints, use one byte for days before + // expiration). + v1 = 0, + // August 2017 (store zoom level and priority as 0-7 values in one byte). + v2 = 1, + latest = v2 +}; +std::vector<uint8_t> Serialize(std::vector<Campaign> const & campaigns, Version const version); std::vector<uint8_t> Serialize(std::vector<Campaign> const & campaigns); std::vector<Campaign> Deserialize(std::vector<uint8_t> const & bytes); + +std::string DebugPrint(local_ads::Version version); } // namespace local_ads diff --git a/local_ads/local_ads_tests/campaign_serialization_test.cpp b/local_ads/local_ads_tests/campaign_serialization_test.cpp index 5479fa5021..40a69017bd 100644 --- a/local_ads/local_ads_tests/campaign_serialization_test.cpp +++ b/local_ads/local_ads_tests/campaign_serialization_test.cpp @@ -9,13 +9,13 @@ using namespace local_ads; namespace { -bool TestSerialization(std::vector<Campaign> const & cs) +bool TestSerialization(std::vector<Campaign> const & cs, Version const v) { - auto const bytes = Serialize(cs); + auto const bytes = Serialize(cs, v); return cs == Deserialize(bytes); } -std::vector<Campaign> GenerateRandomCampaigns(size_t number) +std::vector<Campaign> GenerateCampaignsV1(size_t number) { std::random_device rd; std::mt19937 gen(rd()); @@ -29,7 +29,30 @@ std::vector<Campaign> GenerateRandomCampaigns(size_t number) auto const fid = featureIds(gen); auto const iconid = icons(gen); auto const days = expirationDays(gen); - cs.emplace_back(fid, iconid, days, true /* priorityBit */); + cs.emplace_back(fid, iconid, days); + } + return cs; +} + +std::vector<Campaign> GenerateCampaignsV2(size_t number) +{ + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> featureIds(1, 600000); + std::uniform_int_distribution<> icons(1, 4096); + std::uniform_int_distribution<> expirationDays(1, 30); + std::uniform_int_distribution<> zoomLevels(10, 17); + std::uniform_int_distribution<> priorities(0, 7); + + std::vector<Campaign> cs; + while (number--) + { + auto const fid = featureIds(gen); + auto const iconid = icons(gen); + auto const days = expirationDays(gen); + auto const zoom = zoomLevels(gen); + auto const priority = priorities(gen); + cs.emplace_back(fid, iconid, days, zoom, priority); } return cs; } @@ -38,12 +61,22 @@ std::vector<Campaign> GenerateRandomCampaigns(size_t number) UNIT_TEST(Serialization_Smoke) { TEST(TestSerialization({ - {10, 10, 10, true}, - {1000, 100, 20, true}, - {120003, 456, 15, true} - }), ()); - - TEST(TestSerialization(GenerateRandomCampaigns(100)), ()); - TEST(TestSerialization(GenerateRandomCampaigns(1000)), ()); - TEST(TestSerialization(GenerateRandomCampaigns(10000)), ()); + {10, 10, 10}, + {1000, 100, 20}, + {120003, 456, 15} + }, Version::v1), ()); + + TEST(TestSerialization({ + {10, 10, 10, 10, 0}, + {1000, 100, 20, 17, 7}, + {120003, 456, 15, 13, 6} + }, Version::v2), ()); + + TEST(TestSerialization(GenerateCampaignsV1(100), Version::v1), ()); + TEST(TestSerialization(GenerateCampaignsV1(1000), Version::v1), ()); + TEST(TestSerialization(GenerateCampaignsV1(10000), Version::v1), ()); + + TEST(TestSerialization(GenerateCampaignsV2(100), Version::v2), ()); + TEST(TestSerialization(GenerateCampaignsV2(1000), Version::v2), ()); + TEST(TestSerialization(GenerateCampaignsV2(10000), Version::v2), ()); } diff --git a/local_ads/pylocal_ads/bindings.cpp b/local_ads/pylocal_ads/bindings.cpp index a3639f64c6..c31ea91f01 100644 --- a/local_ads/pylocal_ads/bindings.cpp +++ b/local_ads/pylocal_ads/bindings.cpp @@ -38,11 +38,12 @@ BOOST_PYTHON_MODULE(pylocal_ads) to_python_converter<std::vector<uint8_t>, vector_uint8t_to_str>(); vector_uint8t_from_python_str(); - class_<Campaign>("Campaign", init<uint32_t, uint16_t, uint8_t, bool>()) + class_<Campaign>("Campaign", init<uint32_t, uint16_t, uint8_t, uint8_t, uint8_t>()) .def_readonly("m_featureId", &Campaign::m_featureId) .def_readonly("m_iconId", &Campaign::m_iconId) .def_readonly("m_daysBeforeExpired", &Campaign::m_daysBeforeExpired) - .def_readonly("m_priorityBit", &Campaign::m_priorityBit); + .def_readonly("m_minZoomLevel", &Campaign::m_minZoomLevel) + .def_readonly("m_priority", &Campaign::m_priority); class_<std::vector<Campaign>>("CampaignList") .def(vector_indexing_suite<std::vector<Campaign>>()); diff --git a/local_ads/pylocal_ads/bindings_test.py b/local_ads/pylocal_ads/bindings_test.py new file mode 100644 index 0000000000..4decdc314a --- /dev/null +++ b/local_ads/pylocal_ads/bindings_test.py @@ -0,0 +1,27 @@ +from pylocal_ads import (Campaign, serialize, deserialize) + + +def smoke(): + campaigns = [ + Campaign(10, 10, 10, 10, 0), + Campaign(1000, 100, 20, 17, 7), + Campaign(120003, 456, 15, 13, 6) + ] + + serialized = serialize(campaigns) + result = deserialize(serialized) + + if campaigns.sort() == result.sort(): + return True + + return False + + +def main(): + if smoke(): + print "Smoke OK" + else: + print "Smoke FAIL" + +if __name__ == "__main__": + main() |