MAPSME-5094 localads serialization

This commit is contained in:
Arsentiy Milchakov 2017-07-28 13:35:46 +03:00 committed by Yuri Gorshenin
parent 1c252b7316
commit 84183a6184
6 changed files with 212 additions and 60 deletions

View file

@ -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

View file

@ -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,31 +61,10 @@ std::vector<Integral> ReadData(ByteStream & s, size_t chunksNumber)
return result;
}
} // namespace
namespace local_ads
{
std::vector<uint8_t> Serialize(std::vector<Campaign> const & campaigns)
{
std::vector<uint8_t> buff;
PushBackByteSink<decltype(buff)> dst(buff);
Write(dst, Version::latest);
Write(dst, campaigns.size());
for (auto const & c : campaigns)
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);
return buff;
}
std::vector<Campaign> Deserialize(std::vector<uint8_t> const & bytes)
std::vector<Campaign> DeserializeV1(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.
CHECK_EQUAL(Read<Version>(src), Version::v1, ());
auto const chunksNumber = Read<uint64_t>(src);
auto const featureIds = ReadData<uint32_t>(src, chunksNumber);
@ -91,13 +79,105 @@ std::vector<Campaign> Deserialize(std::vector<uint8_t> const & bytes)
campaigns.reserve(chunksNumber);
for (size_t i = 0; i < chunksNumber; ++i)
{
campaigns.emplace_back(
featureIds[i],
icons[i],
expirations[i],
true /* priorityBit */
);
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, Version const version)
{
std::vector<uint8_t> buff;
PushBackByteSink<decltype(buff)> dst(buff);
Write(dst, version);
Write(dst, campaigns.size());
for (auto const & c : campaigns)
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);
switch (version)
{
case Version::v1: return DeserializeV1(bytes);
case Version::v2: return DeserializeV2(bytes);
default: ASSERT(false, ("Unknown version type"));
}
return {};
}
std::string DebugPrint(local_ads::Version version)
{
using local_ads::Version;
switch (version)
{
case Version::unknown: return "Unknown";
case Version::v1: return "Version 1";
case Version::v2: return "Version 2";
}
}
} // namespace local_ads

View file

@ -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

View file

@ -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}
}), ());
{10, 10, 10},
{1000, 100, 20},
{120003, 456, 15}
}, Version::v1), ());
TEST(TestSerialization(GenerateRandomCampaigns(100)), ());
TEST(TestSerialization(GenerateRandomCampaigns(1000)), ());
TEST(TestSerialization(GenerateRandomCampaigns(10000)), ());
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), ());
}

View file

@ -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>>());

View file

@ -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()