forked from organicmaps/organicmaps
MAPSME-5094 localads serialization
This commit is contained in:
parent
1c252b7316
commit
84183a6184
6 changed files with 212 additions and 60 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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), ());
|
||||
}
|
||||
|
|
|
@ -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>>());
|
||||
|
|
27
local_ads/pylocal_ads/bindings_test.py
Normal file
27
local_ads/pylocal_ads/bindings_test.py
Normal 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()
|
Loading…
Add table
Reference in a new issue