Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mapsme/omim.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt1
-rw-r--r--android/jni/CMakeLists.txt1
-rw-r--r--coding/CMakeLists.txt3
-rw-r--r--coding/serdes_binary_header.hpp60
-rw-r--r--coding/sha1.cpp56
-rw-r--r--coding/sha1.hpp17
-rw-r--r--feature_list/CMakeLists.txt1
-rw-r--r--generator/feature_segments_checker/CMakeLists.txt1
-rw-r--r--generator/generator_tests/CMakeLists.txt1
-rw-r--r--generator/srtm_coverage_checker/CMakeLists.txt1
-rw-r--r--iphone/Maps/Maps.xcodeproj/project.pbxproj4
-rw-r--r--kml/CMakeLists.txt16
-rw-r--r--kml/header_binary.hpp54
-rw-r--r--kml/kml_tests/CMakeLists.txt21
-rw-r--r--kml/kml_tests/serdes_tests.cpp543
-rw-r--r--kml/serdes.cpp593
-rw-r--r--kml/serdes.hpp130
-rw-r--r--kml/serdes_binary.cpp38
-rw-r--r--kml/serdes_binary.hpp211
-rw-r--r--kml/type_utils.hpp103
-rw-r--r--kml/types.hpp280
-rw-r--r--kml/visitors.hpp546
-rw-r--r--map/benchmark_tool/CMakeLists.txt1
-rw-r--r--map/cloud.cpp38
-rw-r--r--map/map_tests/CMakeLists.txt1
-rw-r--r--map/mwm_tests/CMakeLists.txt1
-rw-r--r--map/style_tests/CMakeLists.txt1
-rw-r--r--mapshot/CMakeLists.txt1
-rw-r--r--openlr/openlr_match_quality/openlr_assessment_tool/CMakeLists.txt1
-rw-r--r--qt/CMakeLists.txt1
-rw-r--r--routing/routing_benchmarks/CMakeLists.txt1
-rw-r--r--routing/routing_consistency_tests/CMakeLists.txt1
-rw-r--r--routing/routing_integration_tests/CMakeLists.txt1
-rw-r--r--routing/routing_tests/CMakeLists.txt1
-rw-r--r--search/search_quality/assessment_tool/CMakeLists.txt1
-rw-r--r--storage/storage_integration_tests/CMakeLists.txt1
-rw-r--r--storage/storage_tests/CMakeLists.txt1
-rw-r--r--track_analyzing/track_analyzer/CMakeLists.txt1
-rw-r--r--ugc/binary/header_v0.hpp58
-rw-r--r--xcode/coding/coding.xcodeproj/project.pbxproj12
-rw-r--r--xcode/kml/kml.xcodeproj/project.pbxproj349
-rw-r--r--xcode/kml/kml.xcodeproj/project.xcworkspace/contents.xcworkspacedata7
-rw-r--r--xcode/omim.xcworkspace/contents.xcworkspacedata3
43 files changed, 3075 insertions, 88 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 406490c970..8829b2e60f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -263,6 +263,7 @@ add_subdirectory(tracking)
add_subdirectory(traffic)
add_subdirectory(transit)
add_subdirectory(ugc)
+add_subdirectory(kml)
if (PLATFORM_DESKTOP)
if (BUILD_MAPSHOT)
diff --git a/android/jni/CMakeLists.txt b/android/jni/CMakeLists.txt
index 4dc08a2983..32aa1bcc2e 100644
--- a/android/jni/CMakeLists.txt
+++ b/android/jni/CMakeLists.txt
@@ -111,6 +111,7 @@ target_link_libraries(
ugc
indexer
drape
+ kml
platform
editor
partners_api
diff --git a/coding/CMakeLists.txt b/coding/CMakeLists.txt
index e2c1e6e003..50515fc1dc 100644
--- a/coding/CMakeLists.txt
+++ b/coding/CMakeLists.txt
@@ -68,9 +68,12 @@ set(
reader_wrapper.hpp
reader_writer_ops.cpp
reader_writer_ops.hpp
+ serdes_binary_header.hpp
serdes_json.hpp
simple_dense_coding.cpp
simple_dense_coding.hpp
+ sha1.cpp
+ sha1.hpp
streams.hpp
streams_common.hpp
streams_sink.hpp
diff --git a/coding/serdes_binary_header.hpp b/coding/serdes_binary_header.hpp
new file mode 100644
index 0000000000..54a4d0a09a
--- /dev/null
+++ b/coding/serdes_binary_header.hpp
@@ -0,0 +1,60 @@
+#pragma once
+
+#include "coding/reader.hpp"
+#include "coding/write_to_sink.hpp"
+
+#include <cstdint>
+
+namespace coding
+{
+namespace binary
+{
+struct HeaderSizeOfVisitor
+{
+ void operator()(uint64_t v, char const * /* name */ = nullptr) { m_size += sizeof(v); }
+
+ template <typename R>
+ void operator()(R & r, char const * /* name */ = nullptr)
+ {
+ r.Visit(*this);
+ }
+
+ uint64_t m_size = 0;
+};
+
+template <typename Sink>
+struct HeaderSerVisitor
+{
+ HeaderSerVisitor(Sink & sink) : m_sink(sink) {}
+
+ void operator()(uint64_t v, char const * /* name */ = nullptr) { WriteToSink(m_sink, v); }
+
+ template <typename R>
+ void operator()(R & r, char const * /* name */ = nullptr)
+ {
+ r.Visit(*this);
+ }
+
+ Sink & m_sink;
+};
+
+template <typename Source>
+struct HeaderDesVisitor
+{
+ HeaderDesVisitor(Source & source): m_source(source) {}
+
+ void operator()(uint64_t & v, char const * /* name */ = nullptr)
+ {
+ v = ReadPrimitiveFromSource<uint64_t>(m_source);
+ }
+
+ template <typename R>
+ void operator()(R & r, char const * /* name */ = nullptr)
+ {
+ r.Visit(*this);
+ }
+
+ Source & m_source;
+};
+} // namespace binary
+} // namespace coding
diff --git a/coding/sha1.cpp b/coding/sha1.cpp
new file mode 100644
index 0000000000..4a072e3fc2
--- /dev/null
+++ b/coding/sha1.cpp
@@ -0,0 +1,56 @@
+#include "coding/sha1.hpp"
+
+#include "coding/internal/file_data.hpp"
+#include "coding/file_reader.hpp"
+
+#include "base/assert.hpp"
+#include "base/logging.hpp"
+#include "base/macros.hpp"
+
+#include "3party/liboauthcpp/src/SHA1.h"
+#include "3party/liboauthcpp/src/base64.h"
+
+#include <algorithm>
+
+namespace coding
+{
+// static
+SHA1::Hash SHA1::Calculate(std::string const & filePath)
+{
+ uint32_t constexpr kFileBufferSize = 8192;
+ try
+ {
+ my::FileData file(filePath, my::FileData::OP_READ);
+ uint64_t const fileSize = file.Size();
+
+ CSHA1 sha1;
+ uint64_t currSize = 0;
+ unsigned char buffer[kFileBufferSize];
+ while (currSize < fileSize)
+ {
+ auto const toRead = std::min(kFileBufferSize, static_cast<uint32_t>(fileSize - currSize));
+ file.Read(currSize, buffer, toRead);
+ sha1.Update(buffer, toRead);
+ currSize += toRead;
+ }
+ sha1.Final();
+
+ Hash result;
+ ASSERT_EQUAL(result.size(), ARRAY_SIZE(sha1.m_digest), ());
+ std::copy(std::begin(sha1.m_digest), std::end(sha1.m_digest), std::begin(result));
+ return result;
+ }
+ catch (Reader::Exception const & ex)
+ {
+ LOG(LERROR, ("Error reading file:", filePath, ex.what()));
+ }
+ return {};
+}
+
+// static
+std::string SHA1::CalculateBase64(std::string const & filePath)
+{
+ auto sha1 = Calculate(filePath);
+ return base64_encode(sha1.data(), sha1.size());
+}
+} // coding
diff --git a/coding/sha1.hpp b/coding/sha1.hpp
new file mode 100644
index 0000000000..d46a718028
--- /dev/null
+++ b/coding/sha1.hpp
@@ -0,0 +1,17 @@
+#pragma once
+
+#include <array>
+#include <string>
+
+namespace coding
+{
+class SHA1
+{
+public:
+ static size_t constexpr kHashSizeInBytes = 20;
+ using Hash = std::array<uint8_t, kHashSizeInBytes>;
+
+ static Hash Calculate(std::string const & filePath);
+ static std::string CalculateBase64(std::string const & filePath);
+};
+} // coding
diff --git a/feature_list/CMakeLists.txt b/feature_list/CMakeLists.txt
index f4f7054017..54aa82dc6a 100644
--- a/feature_list/CMakeLists.txt
+++ b/feature_list/CMakeLists.txt
@@ -15,6 +15,7 @@ omim_link_libraries(
storage
ugc
indexer
+ kml
platform
editor
mwm_diff
diff --git a/generator/feature_segments_checker/CMakeLists.txt b/generator/feature_segments_checker/CMakeLists.txt
index 811f2ae958..9710cbf2b1 100644
--- a/generator/feature_segments_checker/CMakeLists.txt
+++ b/generator/feature_segments_checker/CMakeLists.txt
@@ -23,6 +23,7 @@ omim_link_libraries(
indexer
editor
storage
+ kml
platform
geometry
coding
diff --git a/generator/generator_tests/CMakeLists.txt b/generator/generator_tests/CMakeLists.txt
index 3d9a6e80c3..da83ea4e53 100644
--- a/generator/generator_tests/CMakeLists.txt
+++ b/generator/generator_tests/CMakeLists.txt
@@ -45,6 +45,7 @@ omim_link_libraries(
routing_common
transit
editor
+ kml
platform
geometry
coding
diff --git a/generator/srtm_coverage_checker/CMakeLists.txt b/generator/srtm_coverage_checker/CMakeLists.txt
index 34889d4fdb..0e0f93a411 100644
--- a/generator/srtm_coverage_checker/CMakeLists.txt
+++ b/generator/srtm_coverage_checker/CMakeLists.txt
@@ -21,6 +21,7 @@ omim_link_libraries(
traffic
indexer
editor
+ kml
platform
geometry
coding
diff --git a/iphone/Maps/Maps.xcodeproj/project.pbxproj b/iphone/Maps/Maps.xcodeproj/project.pbxproj
index f81ecc4eee..af8d110c25 100644
--- a/iphone/Maps/Maps.xcodeproj/project.pbxproj
+++ b/iphone/Maps/Maps.xcodeproj/project.pbxproj
@@ -347,6 +347,7 @@
4554B6EE1E55F0F30084017F /* drules_proto_vehicle_dark.bin in Resources */ = {isa = PBXBuildFile; fileRef = 4554B6E91E55F02B0084017F /* drules_proto_vehicle_dark.bin */; };
4586D0C41F48121A00DF9CE5 /* libbsdiff.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4586D0C31F48121A00DF9CE5 /* libbsdiff.a */; };
4586D0E71F4813AB00DF9CE5 /* libmwm_diff.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4586D0E61F4813AB00DF9CE5 /* libmwm_diff.a */; };
+ 45CBCCBA20590AAB006B55C2 /* libkml.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 45CBCCBB20590AAB006B55C2 /* libkml.a */; };
45FFD65D1E965EBE00DB854E /* liblocal_ads.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 45FFD65C1E965EBE00DB854E /* liblocal_ads.a */; };
4A300ED51C6DCFD400140018 /* countries-strings in Resources */ = {isa = PBXBuildFile; fileRef = 4A300ED31C6DCFD400140018 /* countries-strings */; };
56C74C391C74A3BC00B71B9F /* MWMInputEmailValidator.mm in Sources */ = {isa = PBXBuildFile; fileRef = 34ABA62F1C2D58F300FE1BEC /* MWMInputEmailValidator.mm */; };
@@ -1266,6 +1267,7 @@
458287C21AD3BE2000BA8940 /* DownloadIndicatorProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DownloadIndicatorProtocol.h; sourceTree = "<group>"; };
4586D0C31F48121A00DF9CE5 /* libbsdiff.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libbsdiff.a; path = ../../xcode/bsdiff/build/Debug/libbsdiff.a; sourceTree = "<group>"; };
4586D0E61F4813AB00DF9CE5 /* libmwm_diff.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libmwm_diff.a; path = ../../xcode/mwm_diff/build/Debug/libmwm_diff.a; sourceTree = "<group>"; };
+ 45CBCCBB20590AAB006B55C2 /* libkml.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libkml.a; sourceTree = BUILT_PRODUCTS_DIR; };
45FFD65C1E965EBE00DB854E /* liblocal_ads.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = liblocal_ads.a; path = "/Users/r.kuznetsov/Dev/Projects/omim/xcode/local_ads/../../../omim-build/xcode/Debug/liblocal_ads.a"; sourceTree = "<absolute>"; };
46F26CD610F623BA00ECCA39 /* EAGLView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = EAGLView.h; sourceTree = "<group>"; };
46F26CD710F623BA00ECCA39 /* EAGLView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = EAGLView.mm; sourceTree = "<group>"; };
@@ -1728,6 +1730,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
+ 45CBCCBA20590AAB006B55C2 /* libkml.a in Frameworks */,
349CFD0720456FEB00569949 /* GoogleMobileAds.framework in Frameworks */,
56EE14D11FE804550036F20C /* libtransit.a in Frameworks */,
345E8F4E1F83984500A826CC /* GoogleSignIn.framework in Frameworks */,
@@ -1891,6 +1894,7 @@
29B97323FDCFA39411CA2CEA /* Frameworks */ = {
isa = PBXGroup;
children = (
+ 45CBCCBB20590AAB006B55C2 /* libkml.a */,
349CFD0620456FEB00569949 /* GoogleMobileAds.framework */,
56EE14D21FE804550036F20C /* libtransit.a */,
4586D0E61F4813AB00DF9CE5 /* libmwm_diff.a */,
diff --git a/kml/CMakeLists.txt b/kml/CMakeLists.txt
new file mode 100644
index 0000000000..97b0b4b43e
--- /dev/null
+++ b/kml/CMakeLists.txt
@@ -0,0 +1,16 @@
+project(kml)
+
+set(
+ SRC
+ header_binary.hpp
+ serdes.cpp
+ serdes.hpp
+ serdes_binary.cpp
+ serdes_binary.hpp
+ type_utils.hpp
+ types.hpp
+ visitors.hpp
+)
+
+add_library(${PROJECT_NAME} ${SRC})
+omim_add_test_subdirectory(kml_tests)
diff --git a/kml/header_binary.hpp b/kml/header_binary.hpp
new file mode 100644
index 0000000000..d34daa3cc9
--- /dev/null
+++ b/kml/header_binary.hpp
@@ -0,0 +1,54 @@
+#pragma once
+
+#include "coding/reader.hpp"
+#include "coding/serdes_binary_header.hpp"
+#include "coding/write_to_sink.hpp"
+
+#include <cstdint>
+
+namespace kml
+{
+namespace binary
+{
+struct Header
+{
+ template <typename Visitor>
+ void Visit(Visitor & visitor)
+ {
+ visitor(m_categoryOffset, "categoryOffset");
+ visitor(m_bookmarksOffset, "bookmarksOffset");
+ visitor(m_tracksOffset, "tracksOffset");
+ visitor(m_stringsOffset, "stringsOffset");
+ visitor(m_eosOffset, "eosOffset");
+ }
+
+ template <typename Sink>
+ void Serialize(Sink & sink)
+ {
+ coding::binary::HeaderSerVisitor<Sink> visitor(sink);
+ visitor(*this);
+ }
+
+ template <typename Source>
+ void Deserialize(Source & source)
+ {
+ coding::binary::HeaderDesVisitor<Source> visitor(source);
+ visitor(*this);
+ }
+
+ // Calculates the size of serialized header in bytes.
+ uint64_t Size()
+ {
+ coding::binary::HeaderSizeOfVisitor visitor;
+ visitor(*this);
+ return visitor.m_size;
+ }
+
+ uint64_t m_categoryOffset = 0;
+ uint64_t m_bookmarksOffset = 0;
+ uint64_t m_tracksOffset = 0;
+ uint64_t m_stringsOffset = 0;
+ uint64_t m_eosOffset = 0;
+};
+} // namespace binary
+} // namespace kml
diff --git a/kml/kml_tests/CMakeLists.txt b/kml/kml_tests/CMakeLists.txt
new file mode 100644
index 0000000000..a0e30270a5
--- /dev/null
+++ b/kml/kml_tests/CMakeLists.txt
@@ -0,0 +1,21 @@
+project(kml_tests)
+
+set(
+ SRC
+ serdes_tests.cpp
+)
+
+omim_add_test(${PROJECT_NAME} ${SRC})
+
+omim_link_libraries(
+ ${PROJECT_NAME}
+ kml
+ platform
+ coding
+ geometry
+ base
+ expat
+ stats_client
+ ${LIBZ}
+)
+link_qt5_core(${PROJECT_NAME})
diff --git a/kml/kml_tests/serdes_tests.cpp b/kml/kml_tests/serdes_tests.cpp
new file mode 100644
index 0000000000..b265cc98ed
--- /dev/null
+++ b/kml/kml_tests/serdes_tests.cpp
@@ -0,0 +1,543 @@
+#include "testing/testing.hpp"
+
+#include "kml/serdes.hpp"
+#include "kml/serdes_binary.hpp"
+
+#include "coding/file_name_utils.hpp"
+#include "coding/file_reader.hpp"
+#include "coding/file_writer.hpp"
+#include "coding/hex.hpp"
+#include "coding/reader.hpp"
+#include "coding/writer.hpp"
+
+#include "base/scope_guard.hpp"
+#include "coding/multilang_utf8_string.hpp"
+
+#include <chrono>
+#include <cstring>
+#include <functional>
+#include <memory>
+#include <sstream>
+#include <vector>
+
+namespace
+{
+char const * kTextKml =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<kml xmlns=\"http://earth.google.com/kml/2.2\">"
+ "<Document>"
+ "<name>MapName</name>"
+ "<description><![CDATA[MapDescription]]></description>"
+ "<visibility>0</visibility>"
+ "<Style id=\"placemark-blue\">"
+ "<IconStyle>"
+ "<Icon>"
+ "<href>http://www.mapswithme.com/placemarks/placemark-blue.png</href>"
+ "</Icon>"
+ "</IconStyle>"
+ "</Style>"
+ "<Style id=\"placemark-brown\">"
+ "<IconStyle>"
+ "<Icon>"
+ "<href>http://www.mapswithme.com/placemarks/placemark-brown.png</href>"
+ "</Icon>"
+ "</IconStyle>"
+ "</Style>"
+ "<Style id=\"placemark-green\">"
+ "<IconStyle>"
+ "<Icon>"
+ "<href>http://www.mapswithme.com/placemarks/placemark-green.png</href>"
+ "</Icon>"
+ "</IconStyle>"
+ "</Style>"
+ "<Style id=\"placemark-orange\">"
+ "<IconStyle>"
+ "<Icon>"
+ "<href>http://www.mapswithme.com/placemarks/placemark-orange.png</href>"
+ "</Icon>"
+ "</IconStyle>"
+ "</Style>"
+ "<Style id=\"placemark-pink\">"
+ "<IconStyle>"
+ "<Icon>"
+ "<href>http://www.mapswithme.com/placemarks/placemark-pink.png</href>"
+ "</Icon>"
+ "</IconStyle>"
+ "</Style>"
+ "<Style id=\"placemark-purple\">"
+ "<IconStyle>"
+ "<Icon>"
+ "<href>http://www.mapswithme.com/placemarks/placemark-purple.png</href>"
+ "</Icon>"
+ "</IconStyle>"
+ "</Style>"
+ "<Style id=\"placemark-red\">"
+ "<IconStyle>"
+ "<Icon>"
+ "<href>http://www.mapswithme.com/placemarks/placemark-red.png</href>"
+ "</Icon>"
+ "</IconStyle>"
+ "</Style>"
+ "<Placemark>"
+ "<name>Nebraska</name>"
+ "<description><![CDATA[]]></description>"
+ "<styleUrl>#placemark-red</styleUrl>"
+ "<Point>"
+ "<coordinates>-99.901810,41.492538,0.000000</coordinates>"
+ "</Point>"
+ "</Placemark>"
+ "<Placemark>"
+ "<name>Monongahela National Forest</name>"
+ "<description><![CDATA[Huttonsville, WV 26273<br>]]></description>"
+ "<styleUrl>#placemark-pink</styleUrl>"
+ "<TimeStamp>"
+ "<when>1986-08-12T07:10:43Z</when>"
+ "</TimeStamp>"
+ "<Point>"
+ "<coordinates>-79.829674,38.627785,0.000000</coordinates>"
+ "</Point>"
+ "</Placemark>"
+ "<Placemark>"
+ "<name>From: Минск, Минская область, Беларусь</name>"
+ "<description><![CDATA[]]></description>"
+ "<styleUrl>#placemark-blue</styleUrl>"
+ "<TimeStamp>"
+ "<when>1998-03-03T03:04:48+01:30</when>"
+ "</TimeStamp>"
+ "<Point>"
+ "<coordinates>27.566765,53.900047,0</coordinates>"
+ "</Point>"
+ "</Placemark>"
+ "<Placemark>"
+ "<name><![CDATA[<MWM & Sons>]]></name>"
+ "<description><![CDATA[Amps & <brackets>]]></description>"
+ "<styleUrl>#placemark-green</styleUrl>"
+ "<TimeStamp>"
+ "<when>2048 bytes in two kilobytes - some invalid timestamp</when>"
+ "</TimeStamp>"
+ "<Point>"
+ "<coordinates>27.551532,53.89306</coordinates>"
+ "</Point>"
+ "</Placemark>"
+ "</Document>"
+ "</kml>";
+
+std::vector<uint8_t> const kBinKml = {
+ 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x28, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x00,
+ 0x01, 0x01, 0x00, 0x02, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE2,
+ 0xAB, 0xEB, 0x96, 0xE2, 0xFE, 0x83, 0xD4, 0x11, 0x00, 0x02, 0x01, 0x00, 0x03, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xF3, 0xC2, 0xFB, 0xF9, 0x01, 0x9E, 0xD0, 0xC7, 0x95, 0x9B, 0x9D, 0xBD, 0xAB, 0x12, 0x00,
+ 0x02, 0x01, 0x00, 0x04, 0x01, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB8, 0xBC, 0xED, 0xA7, 0x03, 0xEA, 0xA4, 0xD0,
+ 0x9C, 0xD9, 0xA7, 0xBE, 0x9A, 0x19, 0x00, 0x02, 0x01, 0x00, 0x06, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xE4, 0xAF, 0xA7, 0xC1, 0xEB, 0xA6, 0xBE, 0x9A, 0x19, 0x00, 0x02, 0x01, 0x00, 0x07, 0x01, 0x00,
+ 0x08, 0x00, 0x54, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x0E, 0x08, 0x1B, 0x1A,
+ 0x41, 0x0C, 0x11, 0x25, 0x3F, 0x00, 0x02, 0x00, 0x01, 0x08, 0x03, 0x02, 0x06, 0x01, 0x03, 0x06,
+ 0x40, 0x05, 0x07, 0x8D, 0x01, 0x06, 0x05, 0x02, 0x07, 0x07, 0x71, 0x09, 0x05, 0x05, 0x0A, 0x07,
+ 0x12, 0x0B, 0x06, 0x5E, 0x0D, 0x06, 0xBA, 0x01, 0x0E, 0x05, 0x04, 0x0F, 0x05, 0x75, 0x11, 0x05,
+ 0x0D, 0x12, 0x06, 0x09, 0x13, 0x07, 0x4E, 0x15, 0x06, 0x91, 0x01, 0x16, 0x06, 0x0B, 0x17, 0x06,
+ 0x73, 0x19, 0x05, 0x06, 0x1A, 0x08, 0x1C, 0x1B, 0x07, 0x63, 0x1D, 0x06, 0xC0, 0x01, 0x1E, 0x05,
+ 0x07, 0x1F, 0x06, 0x77, 0x21, 0x06, 0x0A, 0x22, 0x07, 0x08, 0x23, 0x07, 0x49, 0x25, 0x07, 0x90,
+ 0x01, 0x27, 0x06, 0x72, 0x2A, 0x08, 0x14, 0x2B, 0x06, 0x62, 0x2D, 0x06, 0xBD, 0x01, 0x32, 0x06,
+ 0x0C, 0x33, 0x06, 0x57, 0x35, 0x07, 0xA1, 0x01, 0x36, 0x06, 0x0F, 0x37, 0x07, 0x76, 0x3A, 0x08,
+ 0x31, 0x3B, 0x06, 0x68, 0x3D, 0x06, 0xD1, 0x01, 0x3F, 0x06, 0x79, 0x41, 0x07, 0xB8, 0x01, 0x45,
+ 0x07, 0x8F, 0x01, 0x47, 0x07, 0x74, 0x4A, 0x07, 0x13, 0x53, 0x07, 0x59, 0x5A, 0x07, 0x29, 0x5B,
+ 0x07, 0x6D, 0x62, 0x08, 0x10, 0x63, 0x07, 0x4B, 0x65, 0x07, 0x96, 0x01, 0x6A, 0x08, 0x18, 0x75,
+ 0x07, 0xB7, 0x01, 0x77, 0x07, 0x8A, 0x01, 0x7A, 0x08, 0x3F, 0x81, 0x01, 0x08, 0x0E, 0x9A, 0x01,
+ 0x08, 0x25, 0xAA, 0x01, 0x08, 0x17, 0xBA, 0x01, 0x08, 0x3E, 0xE2, 0x01, 0x08, 0x11, 0xEA, 0x01,
+ 0x08, 0x19, 0xFA, 0x01, 0x08, 0x41, 0xB0, 0x01, 0xD7, 0x64, 0xD6, 0x6E, 0x37, 0x46, 0x07, 0x3D,
+ 0xA1, 0xA8, 0x65, 0x2F, 0x34, 0x01, 0x83, 0xBE, 0x2E, 0x87, 0x3C, 0x64, 0x71, 0x6E, 0xA3, 0xE7,
+ 0xCC, 0xB7, 0x8E, 0x6D, 0xA7, 0xE2, 0xFD, 0x13, 0xBC, 0x9A, 0x51, 0x7D, 0xF2, 0xE9, 0x17, 0x8F,
+ 0x33, 0x91, 0x28, 0xDB, 0xA1, 0x22, 0x5F, 0xB3, 0x14, 0x65, 0xE9, 0x57, 0x92, 0xAC, 0x33, 0xC5,
+ 0xF8, 0x3E, 0x37, 0x35, 0x00, 0x88, 0x1C, 0x96, 0x62, 0x97, 0x17, 0x09, 0xA5, 0x3F, 0x42, 0x18,
+ 0x98, 0xD5, 0x45, 0x96, 0x1D, 0x0A, 0x46, 0x2C, 0xB9, 0x94, 0xFE, 0x35, 0xF7, 0x00, 0x00, 0xF4,
+ 0x00, 0x00, 0x00, 0x00, 0x1A, 0xD0, 0xA2, 0xD9, 0x95, 0x6B, 0xDC, 0x69, 0xEA, 0x2D, 0x52, 0xB0,
+ 0x43, 0xA8, 0x7C, 0xA9, 0x32, 0x0E, 0x01, 0x00, 0x09,
+};
+
+// This function can be used to generate textual representation of vector<uint8_t> like you see above.
+std::string FormatBytesFromBuffer(std::vector<uint8_t> const & buffer)
+{
+ std::stringstream ss;
+ for (size_t i = 1; i <= buffer.size(); i++)
+ {
+ ss << "0x" << NumToHex(buffer[i - 1]) << ", ";
+ if (i % 16 == 0)
+ ss << "\n";
+ }
+ return ss.str();
+}
+
+auto const kDefaultLang = StringUtf8Multilang::kDefaultCode;
+
+kml::CategoryData GenerateCategoryData()
+{
+ kml::CategoryData categoryData;
+ categoryData.m_name[kDefaultLang] = "Test category";
+ categoryData.m_description[kDefaultLang] = "Test description";
+ categoryData.m_visible = true;
+ categoryData.m_authorName = "Maps.Me";
+ categoryData.m_authorId = "12345";
+ categoryData.m_lastModified = std::chrono::system_clock::from_time_t(1000);
+ categoryData.m_accessRules = kml::AccessRules::Public;
+ categoryData.m_tags = {"mountains", "ski", "snowboard"};
+ categoryData.m_toponyms = {"Georgia", "Gudauri"};
+ categoryData.m_languageCodes = {0, 2, 5};
+
+ kml::BookmarkData bookmarkData;
+ bookmarkData.m_id = 10;
+ bookmarkData.m_name[kDefaultLang] = "Test bookmark";
+ bookmarkData.m_description[kDefaultLang] = "Test bookmark description";
+ bookmarkData.m_types = {8, 13, 34, 565};
+ bookmarkData.m_color = {kml::PredefinedColor::Blue, 0};
+ bookmarkData.m_icon = kml::BookmarkIcon::None;
+ bookmarkData.m_viewportScale = 15;
+ bookmarkData.m_timestamp = std::chrono::system_clock::from_time_t(800);
+ bookmarkData.m_point = m2::PointD(45.9242, 56.8679);
+ bookmarkData.m_boundTracks = {0};
+ categoryData.m_bookmarksData.emplace_back(std::move(bookmarkData));
+
+ kml::TrackData trackData;
+ trackData.m_id = 121;
+ trackData.m_localId = 0;
+ trackData.m_name[kDefaultLang] = "Test track";
+ trackData.m_description[kDefaultLang] = "Test track description";
+ trackData.m_layers = {{6.0, {kml::PredefinedColor::None, 0xff0000ff}},
+ {7.0, {kml::PredefinedColor::None, 0x00ff00ff}}};
+ trackData.m_timestamp = std::chrono::system_clock::from_time_t(900);
+ trackData.m_points = {m2::PointD(45.9242, 56.8679), m2::PointD(45.2244, 56.2786),
+ m2::PointD(45.1964, 56.9832)};
+ categoryData.m_tracksData.emplace_back(std::move(trackData));
+
+ return categoryData;
+}
+
+char const * kGeneratedKml =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<kml xmlns=\"http://earth.google.com/kml/2.2\">\n"
+ "<Document>\n"
+ " <Style id=\"placemark-red\">\n"
+ " <IconStyle>\n"
+ " <Icon>\n"
+ " <href>http://mapswith.me/placemarks/placemark-red.png</href>\n"
+ " </Icon>\n"
+ " </IconStyle>\n"
+ " </Style>\n"
+ " <Style id=\"placemark-blue\">\n"
+ " <IconStyle>\n"
+ " <Icon>\n"
+ " <href>http://mapswith.me/placemarks/placemark-blue.png</href>\n"
+ " </Icon>\n"
+ " </IconStyle>\n"
+ " </Style>\n"
+ " <Style id=\"placemark-purple\">\n"
+ " <IconStyle>\n"
+ " <Icon>\n"
+ " <href>http://mapswith.me/placemarks/placemark-purple.png</href>\n"
+ " </Icon>\n"
+ " </IconStyle>\n"
+ " </Style>\n"
+ " <Style id=\"placemark-yellow\">\n"
+ " <IconStyle>\n"
+ " <Icon>\n"
+ " <href>http://mapswith.me/placemarks/placemark-yellow.png</href>\n"
+ " </Icon>\n"
+ " </IconStyle>\n"
+ " </Style>\n"
+ " <Style id=\"placemark-pink\">\n"
+ " <IconStyle>\n"
+ " <Icon>\n"
+ " <href>http://mapswith.me/placemarks/placemark-pink.png</href>\n"
+ " </Icon>\n"
+ " </IconStyle>\n"
+ " </Style>\n"
+ " <Style id=\"placemark-brown\">\n"
+ " <IconStyle>\n"
+ " <Icon>\n"
+ " <href>http://mapswith.me/placemarks/placemark-brown.png</href>\n"
+ " </Icon>\n"
+ " </IconStyle>\n"
+ " </Style>\n"
+ " <Style id=\"placemark-green\">\n"
+ " <IconStyle>\n"
+ " <Icon>\n"
+ " <href>http://mapswith.me/placemarks/placemark-green.png</href>\n"
+ " </Icon>\n"
+ " </IconStyle>\n"
+ " </Style>\n"
+ " <Style id=\"placemark-orange\">\n"
+ " <IconStyle>\n"
+ " <Icon>\n"
+ " <href>http://mapswith.me/placemarks/placemark-orange.png</href>\n"
+ " </Icon>\n"
+ " </IconStyle>\n"
+ " </Style>\n"
+ " <name>Test category</name>\n"
+ " <description>Test description</description>\n"
+ " <visibility>1</visibility>\n"
+ " <Placemark>\n"
+ " <name>Test bookmark</name>\n"
+ " <description>Test bookmark description</description>\n"
+ " <TimeStamp><when>1970-01-01T00:13:20Z</when></TimeStamp>\n"
+ " <styleUrl>#placemark-blue</styleUrl>\n"
+ " <Point><coordinates>45.9242,49.326859</coordinates></Point>\n"
+ " <ExtendedData xmlns:mwm=\"http://mapswith.me\">\n"
+ " <mwm:scale>15</mwm:scale>\n"
+ " </ExtendedData>\n"
+ " </Placemark>\n"
+ " <Placemark>\n"
+ " <name>Test track</name>\n"
+ " <description>Test track description</description>\n"
+ " <Style><LineStyle>\n"
+ " <color>FF0000FF</color>\n"
+ " <width>6</width>\n"
+ " </LineStyle></Style>\n"
+ " <LineString><coordinates>45.9242,49.326859 45.2244,48.941288 45.1964,49.401948</coordinates></LineString>\n"
+ " </Placemark>\n"
+ "</Document>\n"
+ "</kml>";
+} // namespace
+
+// 1. Check text and binary deserialization from the prepared sources in memory.
+UNIT_TEST(Kml_Deserialization_Text_Bin_Memory)
+{
+ UNUSED_VALUE(FormatBytesFromBuffer({}));
+
+ kml::CategoryData dataFromText;
+ try
+ {
+ kml::DeserializerKml des(dataFromText);
+ MemReader reader(kTextKml, strlen(kTextKml));
+ des.Deserialize(reader);
+ }
+ catch (kml::DeserializerKml::DeserializeException const & exc)
+ {
+ TEST(false, ("Exception raised", exc.what()));
+ }
+
+ kml::CategoryData dataFromBin;
+ try
+ {
+ MemReader reader(kBinKml.data(), kBinKml.size());
+ kml::binary::DeserializerKml des(dataFromBin);
+ des.Deserialize(reader);
+ }
+ catch (kml::binary::DeserializerKml::DeserializeException const & exc)
+ {
+ TEST(false, ("Exception raised", exc.what()));
+ }
+
+ TEST_EQUAL(dataFromText, dataFromBin, ());
+}
+
+// 2. Check text serialization to the memory blob and compare with prepared data.
+UNIT_TEST(Kml_Serialization_Text_Memory)
+{
+ kml::CategoryData data;
+ {
+ kml::DeserializerKml des(data);
+ MemReader reader(kTextKml, strlen(kTextKml));
+ des.Deserialize(reader);
+ }
+
+ std::string resultBuffer;
+ {
+ MemWriter<decltype(resultBuffer)> sink(resultBuffer);
+ kml::SerializerKml ser(data);
+ ser.Serialize(sink);
+ }
+
+ kml::CategoryData data2;
+ {
+ kml::DeserializerKml des(data2);
+ MemReader reader(resultBuffer.c_str(), resultBuffer.length());
+ des.Deserialize(reader);
+ }
+
+ TEST_EQUAL(data, data2, ());
+}
+
+// 3. Check binary serialization to the memory blob and compare with prepared data.
+UNIT_TEST(Kml_Serialization_Bin_Memory)
+{
+ kml::CategoryData data;
+ {
+ kml::binary::DeserializerKml des(data);
+ MemReader reader(kBinKml.data(), kBinKml.size());
+ des.Deserialize(reader);
+ }
+
+ std::vector<uint8_t> buffer;
+ {
+ kml::binary::SerializerKml ser(data);
+ MemWriter<decltype(buffer)> sink(buffer);
+ ser.Serialize(sink);
+ }
+
+ TEST_EQUAL(kBinKml, buffer, ());
+
+ kml::CategoryData data2;
+ {
+ kml::binary::DeserializerKml des(data2);
+ MemReader reader(buffer.data(), buffer.size());
+ des.Deserialize(reader);
+ }
+
+ TEST_EQUAL(data, data2, ());
+}
+
+// 4. Check deserialization from the text file.
+UNIT_TEST(Kml_Deserialization_Text_File)
+{
+ string const kmlFile = my::JoinFoldersToPath(GetPlatform().TmpDir(), "tmp.kml");
+ MY_SCOPE_GUARD(fileGuard, std::bind(&FileWriter::DeleteFileX, kmlFile));
+ try
+ {
+ FileWriter file(kmlFile);
+ file.Write(kTextKml, strlen(kTextKml));
+ }
+ catch (FileWriter::Exception const & exc)
+ {
+ TEST(false, ("Exception raised", exc.what()));
+ }
+
+ kml::CategoryData dataFromFile;
+ try
+ {
+ kml::DeserializerKml des(dataFromFile);
+ FileReader reader(kmlFile);
+ des.Deserialize(reader);
+ }
+ catch (FileReader::Exception const & exc)
+ {
+ TEST(false, ("Exception raised", exc.what()));
+ }
+
+ kml::CategoryData dataFromText;
+ {
+ kml::DeserializerKml des(dataFromText);
+ MemReader reader(kTextKml, strlen(kTextKml));
+ des.Deserialize(reader);
+ }
+
+ TEST_EQUAL(dataFromFile, dataFromText, ());
+}
+
+// 5. Check deserialization from the binary file.
+UNIT_TEST(Kml_Deserialization_Bin_File)
+{
+ string const kmbFile = my::JoinFoldersToPath(GetPlatform().TmpDir(), "tmp.kmb");
+ MY_SCOPE_GUARD(fileGuard, std::bind(&FileWriter::DeleteFileX, kmbFile));
+ try
+ {
+ FileWriter file(kmbFile);
+ file.Write(kBinKml.data(), kBinKml.size());
+ }
+ catch (FileWriter::Exception & exc)
+ {
+ TEST(false, ("Exception raised", exc.what()));
+ }
+
+ kml::CategoryData dataFromFile;
+ try
+ {
+ kml::binary::DeserializerKml des(dataFromFile);
+ FileReader reader(kmbFile);
+ des.Deserialize(reader);
+ }
+ catch (FileReader::Exception const & exc)
+ {
+ TEST(false, ("Exception raised", exc.what()));
+ }
+
+ kml::CategoryData dataFromBin;
+ {
+ kml::binary::DeserializerKml des(dataFromBin);
+ MemReader reader(kBinKml.data(), kBinKml.size());
+ des.Deserialize(reader);
+ }
+
+ TEST_EQUAL(dataFromFile, dataFromBin, ());
+}
+
+// 6. Check serialization to the binary file. Here we use generated data.
+// The data in RAM must be completely equal to the data in binary file.
+UNIT_TEST(Kml_Serialization_Bin_File)
+{
+ auto data = GenerateCategoryData();
+
+ string const kmbFile = my::JoinFoldersToPath(GetPlatform().TmpDir(), "tmp.kmb");
+ MY_SCOPE_GUARD(fileGuard, std::bind(&FileWriter::DeleteFileX, kmbFile));
+ try
+ {
+ kml::binary::SerializerKml ser(data);
+ FileWriter writer(kmbFile);
+ ser.Serialize(writer);
+ }
+ catch (FileWriter::Exception const & exc)
+ {
+ TEST(false, ("Exception raised", exc.what()));
+ }
+
+ kml::CategoryData dataFromFile;
+ try
+ {
+ kml::binary::DeserializerKml des(dataFromFile);
+ FileReader reader(kmbFile);
+ des.Deserialize(reader);
+ }
+ catch (FileReader::Exception const & exc)
+ {
+ TEST(false, ("Exception raised", exc.what()));
+ }
+
+ TEST_EQUAL(data, dataFromFile, ());
+}
+
+// 7. Check serialization to the text file. Here we use generated data.
+// The text in the file can be not equal to the original generated data, because
+// text representation does not support all features, e.g. we do not store ids for
+// bookmarks and tracks.
+UNIT_TEST(Kml_Serialization_Text_File)
+{
+ auto data = GenerateCategoryData();
+
+ string const kmlFile = my::JoinFoldersToPath(GetPlatform().TmpDir(), "tmp.kml");
+ MY_SCOPE_GUARD(fileGuard, std::bind(&FileWriter::DeleteFileX, kmlFile));
+ try
+ {
+ kml::SerializerKml ser(data);
+ FileWriter sink(kmlFile);
+ ser.Serialize(sink);
+ }
+ catch (FileWriter::Exception const & exc)
+ {
+ TEST(false, ("Exception raised", exc.what()));
+ }
+
+ kml::CategoryData dataFromFile;
+ try
+ {
+ kml::DeserializerKml des(dataFromFile);
+ FileReader reader(kmlFile);
+ des.Deserialize(reader);
+ }
+ catch (FileReader::Exception const & exc)
+ {
+ TEST(false, ("Exception raised", exc.what()));
+ }
+
+ kml::CategoryData dataFromMemory;
+ {
+ kml::DeserializerKml des(dataFromMemory);
+ MemReader reader(kGeneratedKml, strlen(kGeneratedKml));
+ des.Deserialize(reader);
+ }
+
+ TEST_EQUAL(dataFromFile, dataFromMemory, ());
+}
diff --git a/kml/serdes.cpp b/kml/serdes.cpp
new file mode 100644
index 0000000000..03e00fca9e
--- /dev/null
+++ b/kml/serdes.cpp
@@ -0,0 +1,593 @@
+#include "kml/serdes.hpp"
+
+#include "geometry/mercator.hpp"
+
+#include "coding/hex.hpp"
+#include "coding/multilang_utf8_string.hpp"
+
+#include "base/assert.hpp"
+#include "base/stl_helpers.hpp"
+#include "base/string_utils.hpp"
+#include "base/timer.hpp"
+
+#include <sstream>
+
+using namespace std::string_literals;
+
+namespace kml
+{
+namespace
+{
+std::string const kPlacemark = "Placemark";
+std::string const kStyle = "Style";
+std::string const kDocument = "Document";
+std::string const kStyleMap = "StyleMap";
+std::string const kStyleUrl = "styleUrl";
+std::string const kPair = "Pair";
+
+std::string const kKmlHeader =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<kml xmlns=\"http://earth.google.com/kml/2.2\">\n"
+ "<Document>\n";
+
+std::string const kKmlFooter =
+ "</Document>\n"
+ "</kml>\n";
+
+auto const kDefaultLang = StringUtf8Multilang::kDefaultCode;
+
+std::string PointToString(m2::PointD const & org)
+{
+ double const lon = MercatorBounds::XToLon(org.x);
+ double const lat = MercatorBounds::YToLat(org.y);
+
+ std::ostringstream ss;
+ ss.precision(8);
+
+ ss << lon << "," << lat;
+ return ss.str();
+}
+
+std::string GetLocalizableString(LocalizableString const & s, int8_t lang)
+{
+ auto const it = s.find(lang);
+ if (it == s.cend())
+ return {};
+ return it->second;
+}
+
+PredefinedColor ExtractPlacemarkPredefinedColor(std::string const & s)
+{
+ if (s == "#placemark-red")
+ return PredefinedColor::Red;
+ if (s == "#placemark-blue")
+ return PredefinedColor::Blue;
+ if (s == "#placemark-purple")
+ return PredefinedColor::Purple;
+ if (s == "#placemark-yellow")
+ return PredefinedColor::Yellow;
+ if (s == "#placemark-pink")
+ return PredefinedColor::Pink;
+ if (s == "#placemark-brown")
+ return PredefinedColor::Brown;
+ if (s == "#placemark-green")
+ return PredefinedColor::Green;
+ if (s == "#placemark-orange")
+ return PredefinedColor::Orange;
+
+ // Default color.
+ return PredefinedColor::Red;
+}
+
+std::string GetStyleForPredefinedColor(PredefinedColor color)
+{
+ switch (color)
+ {
+ case PredefinedColor::Red: return "placemark-red";
+ case PredefinedColor::Blue: return "placemark-blue";
+ case PredefinedColor::Purple: return "placemark-purple";
+ case PredefinedColor::Yellow: return "placemark-yellow";
+ case PredefinedColor::Pink: return "placemark-pink";
+ case PredefinedColor::Brown: return "placemark-brown";
+ case PredefinedColor::Green: return "placemark-green";
+ case PredefinedColor::Orange: return "placemark-orange";
+ case PredefinedColor::None:
+ case PredefinedColor::Count:
+ return {};
+ }
+}
+
+template <typename Channel>
+uint32_t ToRGBA(Channel red, Channel green, Channel blue, Channel alpha)
+{
+ return static_cast<uint8_t>(red) << 24 | static_cast<uint8_t>(green) << 16 |
+ static_cast<uint8_t>(blue) << 8 | static_cast<uint8_t>(alpha);
+}
+
+void SaveStringWithCDATA(KmlWriter::WriterWrapper & writer, std::string const & s)
+{
+ // According to kml/xml spec, we need to escape special symbols with CDATA.
+ if (s.find_first_of("<&") != std::string::npos)
+ writer << "<![CDATA[" << s << "]]>";
+ else
+ writer << s;
+}
+
+void SaveStyle(KmlWriter::WriterWrapper & writer, std::string const & style)
+{
+ if (style.empty())
+ return;
+
+ writer << " <Style id=\"" << style << "\">\n"
+ << " <IconStyle>\n"
+ << " <Icon>\n"
+ << " <href>http://mapswith.me/placemarks/" << style << ".png</href>\n"
+ << " </Icon>\n"
+ << " </IconStyle>\n"
+ << " </Style>\n";
+}
+
+void SaveColorToABGR(KmlWriter::WriterWrapper & writer, uint32_t rgba)
+{
+ writer << NumToHex(static_cast<uint8_t>(rgba & 0xFF))
+ << NumToHex(static_cast<uint8_t>((rgba >> 8) & 0xFF))
+ << NumToHex(static_cast<uint8_t>((rgba >> 16) & 0xFF))
+ << NumToHex(static_cast<uint8_t>((rgba >> 24) & 0xFF));
+}
+
+void SaveCategoryData(KmlWriter::WriterWrapper & writer, CategoryData const & categoryData)
+{
+ for (uint8_t i = 0; i < my::Key(PredefinedColor::Count); ++i)
+ SaveStyle(writer, GetStyleForPredefinedColor(static_cast<PredefinedColor>(i)));
+
+ // Use CDATA if we have special symbols in the name.
+ writer << " <name>";
+ SaveStringWithCDATA(writer, GetLocalizableString(categoryData.m_name, kDefaultLang));
+ writer << "</name>\n";
+ if (!categoryData.m_description.empty())
+ {
+ writer << " <description>";
+ SaveStringWithCDATA(writer, GetLocalizableString(categoryData.m_description, kDefaultLang));
+ writer << "</description>\n";
+ }
+
+ writer << " <visibility>" << (categoryData.m_visible ? "1" : "0") <<"</visibility>\n";
+}
+
+void SaveBoormarkData(KmlWriter::WriterWrapper & writer, BookmarkData const & bookmarkData)
+{
+ writer << " <Placemark>\n";
+ writer << " <name>";
+ SaveStringWithCDATA(writer, GetLocalizableString(bookmarkData.m_name, kDefaultLang));
+ writer << "</name>\n";
+
+ if (!bookmarkData.m_description.empty())
+ {
+ writer << " <description>";
+ SaveStringWithCDATA(writer, GetLocalizableString(bookmarkData.m_description, kDefaultLang));
+ writer << "</description>\n";
+ }
+
+ if (bookmarkData.m_timestamp != Timestamp())
+ {
+ auto const ts = std::chrono::system_clock::to_time_t(bookmarkData.m_timestamp);
+ std::string const strTimeStamp = my::TimestampToString(ts);
+ if (strTimeStamp.size() != 20)
+ MYTHROW(KmlWriter::WriteKmlException, ("We always generate fixed length UTC-format timestamp."));
+ writer << " <TimeStamp><when>" << strTimeStamp << "</when></TimeStamp>\n";
+ }
+
+ auto const style = GetStyleForPredefinedColor(bookmarkData.m_color.m_predefinedColor);
+ writer << " <styleUrl>#" << style << "</styleUrl>\n"
+ << " <Point><coordinates>" << PointToString(bookmarkData.m_point)
+ << "</coordinates></Point>\n";
+
+ if (bookmarkData.m_viewportScale != 0)
+ {
+ auto const scale = strings::to_string(static_cast<double>(bookmarkData.m_viewportScale));
+ /// @todo Factor out to separate function to use for other custom params.
+ writer << " <ExtendedData xmlns:mwm=\"http://mapswith.me\">\n"
+ << " <mwm:scale>" << scale << "</mwm:scale>\n"
+ << " </ExtendedData>\n";
+ }
+
+ writer << " </Placemark>\n";
+}
+
+void SaveTrackData(KmlWriter::WriterWrapper & writer, TrackData const & trackData)
+{
+ writer << " <Placemark>\n";
+ writer << " <name>";
+ SaveStringWithCDATA(writer, GetLocalizableString(trackData.m_name, kDefaultLang));
+ writer << "</name>\n";
+
+ if (!trackData.m_description.empty())
+ {
+ writer << " <description>";
+ SaveStringWithCDATA(writer, GetLocalizableString(trackData.m_description, kDefaultLang));
+ writer << "</description>\n";
+ }
+
+ if (trackData.m_layers.empty())
+ MYTHROW(KmlWriter::WriteKmlException, ("Layers list is empty."));
+
+ auto const & layer = trackData.m_layers.front();
+ writer << " <Style><LineStyle>\n";
+ writer << " <color>";
+ SaveColorToABGR(writer, layer.m_color.m_rgba);
+ writer << "</color>\n";
+ writer << " <width>" << strings::to_string(layer.m_lineWidth) << "</width>\n";
+ writer << " </LineStyle></Style>\n";
+
+ writer << " <LineString><coordinates>";
+
+ for (size_t i = 0; i < trackData.m_points.size(); ++i)
+ {
+ writer << PointToString(trackData.m_points[i]);
+ if (i + 1 != trackData.m_points.size())
+ writer << " ";
+ }
+
+ writer << "</coordinates></LineString>\n";
+ writer << " </Placemark>\n";
+}
+} // namespace
+
+KmlWriter::WriterWrapper & KmlWriter::WriterWrapper::operator<<(std::string const & str)
+{
+ m_writer.Write(str.data(), str.length());
+ return *this;
+}
+
+void KmlWriter::Write(CategoryData const & categoryData)
+{
+ m_writer << kKmlHeader;
+
+ // Save category.
+ SaveCategoryData(m_writer, categoryData);
+
+ // Save bookmarks.
+ for (auto const & bookmarkData : categoryData.m_bookmarksData)
+ SaveBoormarkData(m_writer, bookmarkData);
+
+ // Saving tracks.
+ for (auto const & trackData : categoryData.m_tracksData)
+ SaveTrackData(m_writer, trackData);
+
+ m_writer << kKmlFooter;
+}
+
+KmlParser::KmlParser(CategoryData & data) : m_data(data) { Reset(); }
+
+void KmlParser::Reset()
+{
+ m_name.clear();
+ m_description.clear();
+ m_org = {};
+ m_predefinedColor = PredefinedColor::None;
+ m_viewportScale = 0;
+ m_timestamp = {};
+
+ m_color = 0;
+ m_styleId.clear();
+ m_mapStyleId.clear();
+ m_styleUrlKey.clear();
+
+ m_points.clear();
+ m_geometryType = GEOMETRY_TYPE_UNKNOWN;
+}
+
+bool KmlParser::ParsePoint(std::string const & s, char const * delim, m2::PointD & pt)
+{
+ // Order in string is: lon, lat, z.
+ strings::SimpleTokenizer iter(s, delim);
+ if (iter)
+ {
+ double lon;
+ if (strings::to_double(*iter, lon) && MercatorBounds::ValidLon(lon) && ++iter)
+ {
+ double lat;
+ if (strings::to_double(*iter, lat) && MercatorBounds::ValidLat(lat))
+ {
+ pt = MercatorBounds::FromLatLon(lat, lon);
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+void KmlParser::SetOrigin(std::string const & s)
+{
+ m_geometryType = GEOMETRY_TYPE_POINT;
+
+ m2::PointD pt;
+ if (ParsePoint(s, ", \n\r\t", pt))
+ m_org = pt;
+}
+
+void KmlParser::ParseLineCoordinates(std::string const & s, char const * blockSeparator,
+ char const * coordSeparator)
+{
+ m_geometryType = GEOMETRY_TYPE_LINE;
+
+ strings::SimpleTokenizer tupleIter(s, blockSeparator);
+ while (tupleIter)
+ {
+ m2::PointD pt;
+ if (ParsePoint(*tupleIter, coordSeparator, pt))
+ {
+ if (m_points.size() == 0 || !(pt - m_points.back()).IsAlmostZero())
+ m_points.push_back(std::move(pt));
+ }
+ ++tupleIter;
+ }
+}
+
+bool KmlParser::MakeValid()
+{
+ if (GEOMETRY_TYPE_POINT == m_geometryType)
+ {
+ if (MercatorBounds::ValidX(m_org.x) && MercatorBounds::ValidY(m_org.y))
+ {
+ // Set default name.
+ if (m_name.empty())
+ m_name = PointToString(m_org);
+
+ // Set default pin.
+ if (m_predefinedColor == PredefinedColor::None)
+ m_predefinedColor = PredefinedColor::Red;
+
+ return true;
+ }
+ return false;
+ }
+ else if (GEOMETRY_TYPE_LINE == m_geometryType)
+ {
+ return m_points.size() > 1;
+ }
+
+ return false;
+}
+
+void KmlParser::ParseColor(std::string const & value)
+{
+ auto const fromHex = FromHex(value);
+ if (fromHex.size() != 4)
+ return;
+
+ // Color positions in HEX – aabbggrr.
+ m_color = ToRGBA(fromHex[3], fromHex[2], fromHex[1], fromHex[0]);
+}
+
+bool KmlParser::GetColorForStyle(std::string const & styleUrl, uint32_t & color)
+{
+ if (styleUrl.empty())
+ return false;
+
+ // Remove leading '#' symbol
+ auto const it = m_styleUrl2Color.find(styleUrl.substr(1));
+ if (it != m_styleUrl2Color.end())
+ {
+ color = it->second;
+ return true;
+ }
+ return false;
+}
+
+bool KmlParser::Push(std::string const & name)
+{
+ m_tags.push_back(name);
+ return true;
+}
+
+void KmlParser::AddAttr(std::string const & attr, std::string const & value)
+{
+ std::string attrInLowerCase = attr;
+ strings::AsciiToLower(attrInLowerCase);
+
+ if (IsValidAttribute(kStyle, value, attrInLowerCase))
+ m_styleId = value;
+ else if (IsValidAttribute(kStyleMap, value, attrInLowerCase))
+ m_mapStyleId = value;
+}
+
+bool KmlParser::IsValidAttribute(std::string const & type, std::string const & value,
+ std::string const & attrInLowerCase) const
+{
+ return (GetTagFromEnd(0) == type && !value.empty() && attrInLowerCase == "id");
+}
+
+std::string const & KmlParser::GetTagFromEnd(size_t n) const
+{
+ ASSERT_LESS(n, m_tags.size(), ());
+ return m_tags[m_tags.size() - n - 1];
+}
+
+void KmlParser::Pop(std::string const & tag)
+{
+ ASSERT_EQUAL(m_tags.back(), tag, ());
+
+ if (tag == kPlacemark)
+ {
+ if (MakeValid())
+ {
+ if (GEOMETRY_TYPE_POINT == m_geometryType)
+ {
+ BookmarkData data;
+ data.m_name[kDefaultLang] = m_name;
+ data.m_description[kDefaultLang] = m_description;
+ data.m_color.m_predefinedColor = m_predefinedColor;
+ data.m_color.m_rgba = m_color;
+ data.m_viewportScale = m_viewportScale;
+ data.m_timestamp = m_timestamp;
+ data.m_point = m_org;
+ m_data.m_bookmarksData.emplace_back(std::move(data));
+ }
+ else if (GEOMETRY_TYPE_LINE == m_geometryType)
+ {
+ TrackData data;
+ data.m_name[kDefaultLang] = m_name;
+ data.m_description[kDefaultLang] = m_description;
+ TrackLayer layer;
+ layer.m_color.m_predefinedColor = PredefinedColor::None;
+ layer.m_color.m_rgba = m_color;
+ data.m_layers.emplace_back(std::move(layer));
+ data.m_timestamp = m_timestamp;
+ data.m_points = m_points;
+ m_data.m_tracksData.emplace_back(std::move(data));
+ }
+ }
+ Reset();
+ }
+ else if (tag == kStyle)
+ {
+ if (GetTagFromEnd(1) == kDocument)
+ {
+ if (!m_styleId.empty())
+ {
+ m_styleUrl2Color[m_styleId] = m_color;
+ m_color = 0;
+ }
+ }
+ }
+
+ m_tags.pop_back();
+}
+
+void KmlParser::CharData(std::string value)
+{
+ strings::Trim(value);
+
+ size_t const count = m_tags.size();
+ if (count > 1 && !value.empty())
+ {
+ std::string const & currTag = m_tags[count - 1];
+ std::string const & prevTag = m_tags[count - 2];
+ std::string const ppTag = count > 2 ? m_tags[count - 3] : std::string();
+
+ if (prevTag == kDocument)
+ {
+ if (currTag == "name")
+ m_data.m_name[kDefaultLang] = value;
+ else if (currTag == "description")
+ m_data.m_description[kDefaultLang] = value;
+ else if (currTag == "visibility")
+ m_data.m_visible = value != "0";
+ }
+ else if (prevTag == kPlacemark)
+ {
+ if (currTag == "name")
+ {
+ m_name = value;
+ }
+ else if (currTag == "styleUrl")
+ {
+ // Bookmark draw style.
+ m_predefinedColor = ExtractPlacemarkPredefinedColor(value);
+
+ // Check if url is in styleMap map.
+ if (!GetColorForStyle(value, m_color))
+ {
+ // Remove leading '#' symbol.
+ std::string styleId = m_mapStyle2Style[value.substr(1)];
+ if (!styleId.empty())
+ GetColorForStyle(styleId, m_color);
+ }
+ }
+ else if (currTag == "description")
+ {
+ m_description = value;
+ }
+ }
+ else if (prevTag == "LineStyle" && currTag == "color")
+ {
+ ParseColor(value);
+ }
+ else if (ppTag == kStyleMap && prevTag == kPair && currTag == kStyleUrl &&
+ m_styleUrlKey == "normal")
+ {
+ if (!m_mapStyleId.empty())
+ m_mapStyle2Style[m_mapStyleId] = value;
+ }
+ else if (ppTag == kStyleMap && prevTag == kPair && currTag == "key")
+ {
+ m_styleUrlKey = value;
+ }
+ else if (ppTag == kPlacemark)
+ {
+ if (prevTag == "Point")
+ {
+ if (currTag == "coordinates")
+ SetOrigin(value);
+ }
+ else if (prevTag == "LineString")
+ {
+ if (currTag == "coordinates")
+ ParseLineCoordinates(value, " \n\r\t", ",");
+ }
+ else if (prevTag == "gx:Track")
+ {
+ if (currTag == "gx:coord")
+ ParseLineCoordinates(value, "\n\r\t", " ");
+ }
+ else if (prevTag == "ExtendedData")
+ {
+ if (currTag == "mwm:scale")
+ {
+ double scale;
+ if (!strings::to_double(value, scale))
+ scale = 0.0;
+ m_viewportScale = static_cast<uint8_t>(scale);
+ }
+ }
+ else if (prevTag == "TimeStamp")
+ {
+ if (currTag == "when")
+ {
+ auto const ts = my::StringToTimestamp(value);
+ if (ts != my::INVALID_TIME_STAMP)
+ m_timestamp = std::chrono::system_clock::from_time_t(ts);
+ }
+ }
+ else if (currTag == kStyleUrl)
+ {
+ GetColorForStyle(value, m_color);
+ }
+ }
+ else if (ppTag == "MultiGeometry")
+ {
+ if (prevTag == "Point")
+ {
+ if (currTag == "coordinates")
+ SetOrigin(value);
+ }
+ else if (prevTag == "LineString")
+ {
+ if (currTag == "coordinates")
+ ParseLineCoordinates(value, " \n\r\t", ",");
+ }
+ else if (prevTag == "gx:Track")
+ {
+ if (currTag == "gx:coord")
+ ParseLineCoordinates(value, "\n\r\t", " ");
+ }
+ }
+ else if (ppTag == "gx:MultiTrack")
+ {
+ if (prevTag == "gx:Track")
+ {
+ if (currTag == "gx:coord")
+ ParseLineCoordinates(value, "\n\r\t", " ");
+ }
+ }
+ }
+}
+
+DeserializerKml::DeserializerKml(CategoryData & categoryData)
+ : m_categoryData(categoryData)
+{
+ m_categoryData = {};
+}
+} // namespace kml
diff --git a/kml/serdes.hpp b/kml/serdes.hpp
new file mode 100644
index 0000000000..4a68117998
--- /dev/null
+++ b/kml/serdes.hpp
@@ -0,0 +1,130 @@
+#pragma once
+
+#include "kml/types.hpp"
+
+#include "coding/parse_xml.hpp"
+#include "coding/reader.hpp"
+#include "coding/writer.hpp"
+
+#include "base/exception.hpp"
+#include "base/stl_add.hpp"
+
+#include <chrono>
+#include <string>
+
+namespace kml
+{
+class KmlWriter
+{
+public:
+ DECLARE_EXCEPTION(WriteKmlException, RootException);
+
+ class WriterWrapper
+ {
+ public:
+ explicit WriterWrapper(Writer & writer)
+ : m_writer(writer)
+ {}
+ WriterWrapper & operator<<(std::string const & str);
+ private:
+ Writer & m_writer;
+ };
+
+ explicit KmlWriter(Writer & writer)
+ : m_writer(writer)
+ {}
+
+ void Write(CategoryData const & categoryData);
+
+private:
+ WriterWrapper m_writer;
+};
+
+class SerializerKml
+{
+public:
+ explicit SerializerKml(CategoryData const & categoryData)
+ : m_categoryData(categoryData)
+ {}
+
+ template <typename Sink>
+ void Serialize(Sink & sink)
+ {
+ KmlWriter kmlWriter(sink);
+ kmlWriter.Write(m_categoryData);
+ }
+
+private:
+ CategoryData const & m_categoryData;
+};
+
+class KmlParser
+{
+public:
+ explicit KmlParser(CategoryData & data);
+ bool Push(std::string const & name);
+ void AddAttr(std::string const & attr, std::string const & value);
+ bool IsValidAttribute(std::string const & type, std::string const & value,
+ std::string const & attrInLowerCase) const;
+ std::string const & GetTagFromEnd(size_t n) const;
+ void Pop(std::string const & tag);
+ void CharData(std::string value);
+
+private:
+ enum GeometryType
+ {
+ GEOMETRY_TYPE_UNKNOWN,
+ GEOMETRY_TYPE_POINT,
+ GEOMETRY_TYPE_LINE
+ };
+
+ void Reset();
+ bool ParsePoint(std::string const & s, char const * delim, m2::PointD & pt);
+ void SetOrigin(std::string const & s);
+ void ParseLineCoordinates(std::string const & s, char const * blockSeparator,
+ char const * coordSeparator);
+ bool MakeValid();
+ void ParseColor(std::string const &value);
+ bool GetColorForStyle(std::string const & styleUrl, uint32_t & color);
+
+ CategoryData & m_data;
+
+ std::vector<std::string> m_tags;
+ GeometryType m_geometryType;
+ std::vector<m2::PointD> m_points;
+ uint32_t m_color;
+
+ std::string m_styleId;
+ std::string m_mapStyleId;
+ std::string m_styleUrlKey;
+ std::map<std::string, uint32_t> m_styleUrl2Color;
+ std::map<std::string, std::string> m_mapStyle2Style;
+
+ std::string m_name;
+ std::string m_description;
+ PredefinedColor m_predefinedColor;
+ Timestamp m_timestamp;
+ m2::PointD m_org;
+ uint8_t m_viewportScale;
+};
+
+class DeserializerKml
+{
+public:
+ DECLARE_EXCEPTION(DeserializeException, RootException);
+
+ explicit DeserializerKml(CategoryData & categoryData);
+
+ template <typename ReaderType>
+ void Deserialize(ReaderType const & reader)
+ {
+ NonOwningReaderSource src(reader);
+ KmlParser parser(m_categoryData);
+ if (!ParseXML(src, parser, true))
+ MYTHROW(DeserializeException, ("Could not parse KML."));
+ }
+
+private:
+ CategoryData & m_categoryData;
+};
+} // namespace kml
diff --git a/kml/serdes_binary.cpp b/kml/serdes_binary.cpp
new file mode 100644
index 0000000000..b4fb76b764
--- /dev/null
+++ b/kml/serdes_binary.cpp
@@ -0,0 +1,38 @@
+#include "kml/serdes_binary.hpp"
+
+namespace kml
+{
+namespace binary
+{
+SerializerKml::SerializerKml(CategoryData & data)
+ : m_data(data)
+{
+ ClearCollectionIndex();
+
+ // Collect all strings and substitute each for index.
+ auto const avgSz = data.m_bookmarksData.size() * 2 + data.m_tracksData.size() * 2 + 10;
+ LocalizableStringCollector collector(avgSz);
+ CollectorVisitor<decltype(collector)> visitor(collector);
+ visitor(m_data);
+ m_strings = collector.StealCollection();
+}
+
+SerializerKml::~SerializerKml()
+{
+ ClearCollectionIndex();
+}
+
+void SerializerKml::ClearCollectionIndex()
+{
+ LocalizableStringCollector collector(0);
+ CollectorVisitor<decltype(collector)> clearVisitor(collector, true /* clear index */);
+ clearVisitor(m_data);
+}
+
+DeserializerKml::DeserializerKml(CategoryData & data)
+ : m_data(data)
+{
+ m_data = {};
+}
+} // namespace binary
+} // namespace kml
diff --git a/kml/serdes_binary.hpp b/kml/serdes_binary.hpp
new file mode 100644
index 0000000000..c11e73f964
--- /dev/null
+++ b/kml/serdes_binary.hpp
@@ -0,0 +1,211 @@
+#pragma once
+
+#include "kml/header_binary.hpp"
+#include "kml/types.hpp"
+#include "kml/visitors.hpp"
+
+#include "coding/read_write_utils.hpp"
+#include "coding/sha1.hpp"
+#include "coding/text_storage.hpp"
+
+#include "platform/platform.hpp"
+
+#include <string>
+#include <vector>
+
+namespace kml
+{
+namespace binary
+{
+enum class Version : uint8_t
+{
+ V0 = 0,
+ Latest = V0
+};
+
+class SerializerKml
+{
+public:
+ explicit SerializerKml(CategoryData & data);
+ ~SerializerKml();
+
+ void ClearCollectionIndex();
+
+ template <typename Sink>
+ void Serialize(Sink & sink)
+ {
+ // Write format version.
+ WriteToSink(sink, Version::Latest);
+
+ auto const startPos = sink.Pos();
+
+ // Reserve place for the header.
+ Header header;
+ WriteZeroesToSink(sink, header.Size());
+
+ // Serialize category.
+ header.m_categoryOffset = sink.Pos() - startPos;
+ SerializeCategory(sink);
+
+ // Serialize bookmarks.
+ header.m_bookmarksOffset = sink.Pos() - startPos;
+ SerializeBookmarks(sink);
+
+ // Serialize tracks.
+ header.m_tracksOffset = sink.Pos() - startPos;
+ SerializeTracks(sink);
+
+ // Serialize strings.
+ header.m_stringsOffset = sink.Pos() - startPos;
+ SerializeStrings(sink);
+
+ // Fill header.
+ header.m_eosOffset = sink.Pos() - startPos;
+ sink.Seek(startPos);
+ header.Serialize(sink);
+ sink.Seek(startPos + header.m_eosOffset);
+ }
+
+ template <typename Sink>
+ void SerializeCategory(Sink & sink)
+ {
+ CategorySerializerVisitor<Sink> visitor(sink);
+ visitor(m_data);
+ }
+
+ template <typename Sink>
+ void SerializeBookmarks(Sink & sink)
+ {
+ BookmarkSerializerVisitor<Sink> visitor(sink);
+ visitor(m_data.m_bookmarksData);
+ }
+
+ template <typename Sink>
+ void SerializeTracks(Sink & sink)
+ {
+ BookmarkSerializerVisitor<Sink> visitor(sink);
+ visitor(m_data.m_tracksData);
+ }
+
+ // Serializes texts in a compressed storage with block access.
+ template <typename Sink>
+ void SerializeStrings(Sink & sink)
+ {
+ coding::BlockedTextStorageWriter<Sink> writer(sink, 200000 /* blockSize */);
+ for (auto const & str : m_strings)
+ writer.Append(str);
+ }
+
+private:
+ CategoryData & m_data;
+ std::vector<std::string> m_strings;
+};
+
+class DeserializerKml
+{
+public:
+ DECLARE_EXCEPTION(DeserializeException, RootException);
+
+ explicit DeserializerKml(CategoryData & data);
+
+ template <typename ReaderType>
+ void Deserialize(ReaderType const & reader)
+ {
+ // Check version.
+ NonOwningReaderSource source(reader);
+ auto const v = ReadPrimitiveFromSource<Version>(source);
+ if (v != Version::Latest)
+ MYTHROW(DeserializeException, ("Incorrect file version."));
+
+ auto subReader = reader.CreateSubReader(source.Pos(), source.Size());
+ InitializeIfNeeded(*subReader);
+
+ // Deserialize category.
+ {
+ auto categorySubReader = CreateCategorySubReader(*subReader);
+ NonOwningReaderSource src(*categorySubReader);
+ CategoryDeserializerVisitor<decltype(src)> visitor(src);
+ visitor(m_data);
+ }
+
+ // Deserialize bookmarks.
+ {
+ auto bookmarkSubReader = CreateBookmarkSubReader(*subReader);
+ NonOwningReaderSource src(*bookmarkSubReader);
+ BookmarkDeserializerVisitor<decltype(src)> visitor(src);
+ visitor(m_data.m_bookmarksData);
+ }
+
+ // Deserialize tracks.
+ {
+ auto trackSubReader = CreateTrackSubReader(*subReader);
+ NonOwningReaderSource src(*trackSubReader);
+ BookmarkDeserializerVisitor<decltype(src)> visitor(src);
+ visitor(m_data.m_tracksData);
+ }
+
+ // Deserialize strings.
+ {
+ auto textsSubReader = CreateStringsSubReader(*subReader);
+ coding::BlockedTextStorage<Reader> strings(*textsSubReader);
+ DeserializedStringCollector<Reader> collector(strings);
+ CollectorVisitor<decltype(collector)> visitor(collector);
+ visitor(m_data);
+ CollectorVisitor<decltype(collector)> clearVisitor(collector,
+ true /* clear index */);
+ clearVisitor(m_data);
+ }
+ }
+
+private:
+ template <typename ReaderType>
+ void InitializeIfNeeded(ReaderType const & reader)
+ {
+ if (m_initialized)
+ return;
+
+ NonOwningReaderSource source(reader);
+ m_header.Deserialize(source);
+ m_initialized = true;
+ }
+
+ template <typename ReaderType>
+ std::unique_ptr<Reader> CreateSubReader(ReaderType const & reader,
+ uint64_t startPos, uint64_t endPos)
+ {
+ ASSERT(m_initialized, ());
+ ASSERT_GREATER_OR_EQUAL(endPos, startPos, ());
+ auto const size = endPos - startPos;
+ return reader.CreateSubReader(startPos, size);
+ }
+
+ template <typename ReaderType>
+ std::unique_ptr<Reader> CreateCategorySubReader(ReaderType const & reader)
+ {
+ return CreateSubReader(reader, m_header.m_categoryOffset, m_header.m_bookmarksOffset);
+ }
+
+ template <typename ReaderType>
+ std::unique_ptr<Reader> CreateBookmarkSubReader(ReaderType const & reader)
+ {
+ return CreateSubReader(reader, m_header.m_bookmarksOffset, m_header.m_tracksOffset);
+ }
+
+ template <typename ReaderType>
+ std::unique_ptr<Reader> CreateTrackSubReader(ReaderType const & reader)
+ {
+ return CreateSubReader(reader, m_header.m_tracksOffset, m_header.m_stringsOffset);
+ }
+
+ template <typename ReaderType>
+ std::unique_ptr<Reader> CreateStringsSubReader(ReaderType const & reader)
+ {
+ return CreateSubReader(reader, m_header.m_stringsOffset, m_header.m_eosOffset);
+ }
+
+ CategoryData & m_data;
+ Header m_header;
+ bool m_initialized = false;
+};
+} // namespace binary
+} // namespace kml
diff --git a/kml/type_utils.hpp b/kml/type_utils.hpp
new file mode 100644
index 0000000000..c2ef6d22d0
--- /dev/null
+++ b/kml/type_utils.hpp
@@ -0,0 +1,103 @@
+#pragma once
+
+#include "geometry/point2d.hpp"
+
+#include <cstdint>
+#include <chrono>
+#include <ctime>
+#include <map>
+#include <sstream>
+#include <vector>
+#include <unordered_map>
+
+namespace kml
+{
+using Timestamp = std::chrono::time_point<std::chrono::system_clock>;
+using LocalizableString = std::unordered_map<int8_t, std::string>;
+using LocalizableStringSubIndex = std::map<int8_t, uint32_t>;
+using LocalizableStringIndex = std::vector<LocalizableStringSubIndex>;
+using Id = uint64_t;
+using LocalId = uint8_t;
+
+inline uint64_t ToSecondsSinceEpoch(Timestamp const & time)
+{
+ auto const s = std::chrono::duration_cast<std::chrono::seconds>(time.time_since_epoch());
+ return static_cast<uint64_t>(s.count());
+}
+
+inline Timestamp FromSecondsSinceEpoch(uint64_t seconds)
+{
+ return Timestamp(std::chrono::seconds(seconds));
+}
+
+inline bool IsEqual(std::vector<m2::PointD> const & v1,
+ std::vector<m2::PointD> const & v2)
+{
+ if (v1.size() != v2.size())
+ return false;
+
+ double constexpr kEps = 1e-5;
+ for (size_t i = 0; i < v1.size(); ++i)
+ {
+ if (!v1[i].EqualDxDy(v2[i], kEps))
+ return false;
+ }
+
+ return true;
+}
+
+inline bool IsEqual(Timestamp const & ts1, Timestamp const & ts2)
+{
+ return ToSecondsSinceEpoch(ts1) == ToSecondsSinceEpoch(ts2);
+}
+
+uint32_t constexpr kEmptyStringId = 0;
+double constexpr kMinLineWidth = 0.0;
+double constexpr kMaxLineWidth = 100.0;
+
+#define DECLARE_COLLECTABLE(IndexType, ...) \
+ IndexType m_collectionIndex; \
+ template <typename Collector> \
+ void Collect(Collector & collector) \
+ { \
+ collector.Collect(m_collectionIndex, __VA_ARGS__); \
+ } \
+ void ClearCollectionIndex() \
+ { \
+ m_collectionIndex.clear(); \
+ } \
+
+#define VISITOR_COLLECTABLE visitor(m_collectionIndex, "collectionIndex")
+
+#define SKIP_VISITING(Type) void operator()(Type, char const * = nullptr) {}
+
+inline std::string DebugPrint(LocalizableString const & str)
+{
+ std::ostringstream os;
+ os << "[";
+ for (auto it = str.cbegin(); it != str.end();)
+ {
+ os << static_cast<uint32_t>(it->first) << ": " << it->second;
+ ++it;
+ if (it != str.end())
+ os << ", ";
+ }
+ os << "]";
+ return os.str();
+}
+
+inline std::string DebugPrint(Timestamp const & ts)
+{
+ auto t = std::chrono::system_clock::to_time_t(ts);
+ return std::ctime(&t);
+}
+
+inline std::string DebugPrint(m2::PointD const & pt)
+{
+ std::ostringstream os;
+ os << "[" << pt.x << ", " << pt.y << "]";
+ return os.str();
+}
+} // namespace kml
+
+using kml::DebugPrint;
diff --git a/kml/types.hpp b/kml/types.hpp
new file mode 100644
index 0000000000..17cc65ad31
--- /dev/null
+++ b/kml/types.hpp
@@ -0,0 +1,280 @@
+#pragma once
+
+#include "kml/type_utils.hpp"
+
+#include "base/visitor.hpp"
+
+#include <cmath>
+#include <ios>
+#include <memory>
+#include <sstream>
+#include <string>
+#include <vector>
+
+namespace kml
+{
+enum class PredefinedColor : uint8_t
+{
+ None = 0,
+ Red,
+ Blue,
+ Purple,
+ Yellow,
+ Pink,
+ Brown,
+ Green,
+ Orange,
+
+ Count
+};
+
+inline std::string DebugPrint(PredefinedColor color)
+{
+ switch (color)
+ {
+ case PredefinedColor::None: return "None";
+ case PredefinedColor::Red: return "Red";
+ case PredefinedColor::Blue: return "Blue";
+ case PredefinedColor::Purple: return "Purple";
+ case PredefinedColor::Yellow: return "Yellow";
+ case PredefinedColor::Pink: return "Pink";
+ case PredefinedColor::Brown: return "Brown";
+ case PredefinedColor::Green: return "Green";
+ case PredefinedColor::Orange: return "Orange";
+ case PredefinedColor::Count: return {};
+ }
+}
+
+enum class AccessRules : uint8_t
+{
+ Private = 0,
+ Public,
+
+ Count
+};
+
+inline std::string DebugPrint(AccessRules accessRules)
+{
+ switch (accessRules)
+ {
+ case AccessRules::Private: return "Private";
+ case AccessRules::Public: return "Public";
+ case AccessRules::Count: return {};
+ }
+}
+
+enum class BookmarkIcon : uint16_t
+{
+ None = 0,
+
+ Count
+};
+
+inline std::string DebugPrint(BookmarkIcon icon)
+{
+ switch (icon)
+ {
+ case BookmarkIcon::None: return "None";
+ case BookmarkIcon::Count: return {};
+ }
+}
+
+struct ColorData
+{
+ DECLARE_VISITOR_AND_DEBUG_PRINT(ColorData, visitor(m_predefinedColor, "predefinedColor"),
+ visitor(m_rgba, "rgba"))
+
+ bool operator==(ColorData const & data) const
+ {
+ return m_predefinedColor == data.m_predefinedColor && m_rgba == data.m_rgba;
+ }
+
+ bool operator!=(ColorData const & data) const
+ {
+ return !operator==(data);
+ }
+
+ // Predefined color.
+ PredefinedColor m_predefinedColor = PredefinedColor::None;
+ // Color in RGBA format.
+ uint32_t m_rgba = 0;
+};
+
+struct BookmarkData
+{
+ DECLARE_VISITOR_AND_DEBUG_PRINT(BookmarkData, visitor(m_id, "id"),
+ visitor(m_name, "name"),
+ visitor(m_description, "description"),
+ visitor(m_types, "types"),
+ visitor(m_color, "color"),
+ visitor(m_icon, "icon"),
+ visitor(m_viewportScale, "viewportScale"),
+ visitor(m_timestamp, "timestamp"),
+ visitor(m_point, "point"),
+ visitor(m_boundTracks, "boundTracks"),
+ VISITOR_COLLECTABLE)
+
+ DECLARE_COLLECTABLE(LocalizableStringIndex, m_name, m_description)
+
+ bool operator==(BookmarkData const & data) const
+ {
+ double constexpr kEps = 1e-5;
+ return m_id == data.m_id && m_name == data.m_name &&
+ m_description == data.m_description &&
+ m_color == data.m_color && m_icon == data.m_icon &&
+ m_viewportScale == data.m_viewportScale &&
+ IsEqual(m_timestamp, data.m_timestamp) &&
+ m_point.EqualDxDy(data.m_point, kEps) && m_types == data.m_types &&
+ m_boundTracks == data.m_boundTracks;
+ }
+
+ bool operator!=(BookmarkData const & data) const
+ {
+ return !operator==(data);
+ }
+
+ // Unique id.
+ Id m_id = 0;
+ // Bookmark's name.
+ LocalizableString m_name;
+ // Bookmark's description.
+ LocalizableString m_description;
+ // Bookmark's types.
+ std::vector<uint32_t> m_types;
+ // Bookmark's color.
+ ColorData m_color;
+ // Bookmark's icon.
+ BookmarkIcon m_icon = BookmarkIcon::None;
+ // Viewport scale. 0 is a default value (no scale set).
+ uint8_t m_viewportScale = 0;
+ // Creation timestamp.
+ Timestamp m_timestamp = {};
+ // Coordinates in mercator.
+ m2::PointD m_point;
+ // Bound tracks (vector contains local track ids).
+ std::vector<LocalId> m_boundTracks;
+};
+
+struct TrackLayer
+{
+ DECLARE_VISITOR_AND_DEBUG_PRINT(TrackLayer, visitor(m_lineWidth, "lineWidth"),
+ visitor(m_color, "color"))
+
+ bool operator==(TrackLayer const & layer) const
+ {
+ double constexpr kEps = 1e-5;
+ return m_color == layer.m_color && fabs(m_lineWidth - layer.m_lineWidth) < kEps;
+ }
+
+ bool operator!=(TrackLayer const & layer) const
+ {
+ return !operator==(layer);
+ }
+
+ // Line width in pixels. Valid range is [kMinLineWidth; kMaxLineWidth].
+ double m_lineWidth = 5.0;
+ // Layer's color.
+ ColorData m_color;
+};
+
+struct TrackData
+{
+ DECLARE_VISITOR_AND_DEBUG_PRINT(TrackData, visitor(m_id, "id"),
+ visitor(m_localId, "localId"),
+ visitor(m_name, "name"),
+ visitor(m_description, "description"),
+ visitor(m_layers, "layers"),
+ visitor(m_timestamp, "timestamp"),
+ visitor(m_points, "points"),
+ VISITOR_COLLECTABLE)
+
+ DECLARE_COLLECTABLE(LocalizableStringIndex, m_name, m_description)
+
+ bool operator==(TrackData const & data) const
+ {
+ return m_id == data.m_id && m_localId == data.m_localId && m_name == data.m_name &&
+ m_description == data.m_description && m_layers == data.m_layers &&
+ IsEqual(m_timestamp, data.m_timestamp) && IsEqual(m_points, data.m_points);
+ }
+
+ bool operator!=(TrackData const & data) const
+ {
+ return !operator==(data);
+ }
+
+ // Unique id.
+ Id m_id = 0;
+ // Local track id.
+ LocalId m_localId = 0;
+ // Track's name.
+ LocalizableString m_name;
+ // Track's description.
+ LocalizableString m_description;
+ // Layers.
+ std::vector<TrackLayer> m_layers;
+ // Creation timestamp.
+ Timestamp m_timestamp = {};
+ // Points.
+ std::vector<m2::PointD> m_points;
+};
+
+struct CategoryData
+{
+ DECLARE_VISITOR_AND_DEBUG_PRINT(CategoryData, visitor(m_name, "name"),
+ visitor(m_description, "description"),
+ visitor(m_bookmarksData, "bookmarks"),
+ visitor(m_tracksData, "tracks"),
+ visitor(m_visible, "visible"),
+ visitor(m_authorName, "authorName"),
+ visitor(m_authorId, "authorId"),
+ visitor(m_lastModified, "lastModified"),
+ visitor(m_accessRules, "accessRules"),
+ visitor(m_tags, "tags"),
+ visitor(m_toponyms, "toponyms"),
+ visitor(m_languageCodes, "languageCodes"),
+ VISITOR_COLLECTABLE)
+
+ DECLARE_COLLECTABLE(LocalizableStringIndex, m_name, m_description,
+ m_authorName, m_authorId, m_tags, m_toponyms)
+
+ bool operator==(CategoryData const & data) const
+ {
+ return m_name == data.m_name && m_description == data.m_description &&
+ m_visible == data.m_visible && m_accessRules == data.m_accessRules &&
+ m_authorName == data.m_authorName && m_authorId == data.m_authorId &&
+ IsEqual(m_lastModified, data.m_lastModified) && m_tags == data.m_tags &&
+ m_toponyms == data.m_toponyms && m_languageCodes == data.m_languageCodes &&
+ m_bookmarksData == data.m_bookmarksData && m_tracksData == data.m_tracksData;
+ }
+
+ bool operator!=(CategoryData const & data) const
+ {
+ return !operator==(data);
+ }
+
+ // Category's name.
+ LocalizableString m_name;
+ // Category's description.
+ LocalizableString m_description;
+ // Bookmarks collection.
+ std::vector<BookmarkData> m_bookmarksData;
+ // Tracks collection.
+ std::vector<TrackData> m_tracksData;
+ // Collection visibility.
+ bool m_visible = true;
+ // Author's name.
+ std::string m_authorName;
+ // Author's id.
+ std::string m_authorId;
+ // Last modification timestamp.
+ Timestamp m_lastModified;
+ // Access rules.
+ AccessRules m_accessRules = AccessRules::Private;
+ // Collection of tags.
+ std::vector<std::string> m_tags;
+ // Collection of toponyms.
+ std::vector<std::string> m_toponyms;
+ // Language codes.
+ std::vector<int8_t> m_languageCodes;
+};
+} // namespace kml
diff --git a/kml/visitors.hpp b/kml/visitors.hpp
new file mode 100644
index 0000000000..c80525d70a
--- /dev/null
+++ b/kml/visitors.hpp
@@ -0,0 +1,546 @@
+#pragma once
+
+#include "kml/types.hpp"
+
+#include "coding/point_to_integer.hpp"
+#include "coding/text_storage.hpp"
+#include "coding/varint.hpp"
+
+#include "base/bits.hpp"
+
+#include <algorithm>
+#include <vector>
+
+namespace kml
+{
+template <typename Collector>
+class CollectorVisitor
+{
+public:
+ explicit CollectorVisitor(Collector & collector, bool clearIndex = false)
+ : m_collector(collector)
+ , m_clearIndex(clearIndex)
+ {}
+
+ template <typename T>
+ void PerformAction(T & t)
+ {
+ if (m_clearIndex)
+ t.ClearCollectionIndex();
+ else
+ t.Collect(m_collector);
+ }
+
+ void operator()(CategoryData & t, char const * /* name */ = nullptr)
+ {
+ PerformAction(t);
+ t.Visit(*this);
+ }
+
+ void operator()(BookmarkData & t, char const * /* name */ = nullptr)
+ {
+ PerformAction(t);
+ t.Visit(*this);
+ }
+
+ void operator()(TrackData & t, char const * /* name */ = nullptr)
+ {
+ PerformAction(t);
+ t.Visit(*this);
+ }
+
+ template <typename T>
+ void operator()(T & t, char const * /* name */ = nullptr) {}
+
+ template <typename T>
+ void operator()(std::vector<T> & vs, char const * /* name */ = nullptr)
+ {
+ for (auto & v : vs)
+ (*this)(v);
+ }
+
+private:
+ Collector & m_collector;
+ bool const m_clearIndex;
+};
+
+class LocalizableStringCollector
+{
+public:
+ explicit LocalizableStringCollector(size_t reservedCollectionSize)
+ {
+ m_collection.reserve(reservedCollectionSize + 1);
+ m_collection.emplace_back(std::string());
+ }
+
+ template <typename... OtherStrings>
+ void Collect(LocalizableStringIndex & index, LocalizableString const & str,
+ OtherStrings const & ... args)
+ {
+ index.emplace_back(LocalizableStringSubIndex());
+ for (auto const & p : str)
+ CollectString(index.back(), p.first, p.second);
+
+ Collect(index, args...);
+ }
+
+ template <typename... OtherStrings>
+ void Collect(LocalizableStringIndex & index, std::string const & str,
+ OtherStrings const & ... args)
+ {
+ int8_t constexpr kFakeIndex = 0;
+ index.emplace_back(LocalizableStringSubIndex());
+ CollectString(index.back(), kFakeIndex, str);
+
+ Collect(index, args...);
+ }
+
+ template <typename... OtherStrings>
+ void Collect(LocalizableStringIndex & index,
+ std::vector<std::string> const & stringsArray,
+ OtherStrings const & ... args)
+ {
+ index.emplace_back(LocalizableStringSubIndex());
+ auto constexpr kMaxSize = static_cast<size_t>(std::numeric_limits<int8_t>::max());
+ auto const sz = std::min(stringsArray.size(), kMaxSize);
+ for (size_t i = 0; i < sz; ++i)
+ CollectString(index.back(), static_cast<int8_t>(i), stringsArray[i]);
+
+ Collect(index, args...);
+ }
+
+ template <typename...>
+ void Collect(LocalizableStringIndex & index) {}
+
+ std::vector<std::string> && StealCollection() { return std::move(m_collection); }
+
+private:
+ void CollectString(LocalizableStringSubIndex & subIndex, int8_t code,
+ std::string const & str)
+ {
+ if (str.empty())
+ {
+ subIndex.insert(std::make_pair(code, kEmptyStringId));
+ }
+ else
+ {
+ subIndex.insert(std::make_pair(code, m_counter));
+ m_counter++;
+ m_collection.push_back(str);
+ }
+ }
+
+ uint32_t m_counter = kEmptyStringId + 1;
+ std::vector<std::string> m_collection;
+};
+
+namespace binary
+{
+template <typename Sink>
+inline void WriteLocalizableStringIndex(Sink & sink, LocalizableStringIndex const & index)
+{
+ WriteVarUint(sink, static_cast<uint32_t>(index.size()));
+ for (auto const & subIndex : index)
+ {
+ WriteVarUint(sink, static_cast<uint32_t>(subIndex.size()));
+ for (auto const & p : subIndex)
+ {
+ WriteToSink(sink, p.first);
+ WriteVarUint(sink, p.second);
+ }
+ }
+}
+
+template <typename Sink>
+class CategorySerializerVisitor
+{
+public:
+ explicit CategorySerializerVisitor(Sink & sink)
+ : m_sink(sink)
+ {}
+
+ void operator()(LocalizableStringIndex const & index, char const * /* name */ = nullptr)
+ {
+ WriteLocalizableStringIndex(m_sink, index);
+ }
+
+ void operator()(bool b, char const * /* name */ = nullptr)
+ {
+ (*this)(static_cast<uint8_t>(b));
+ }
+
+ void operator()(AccessRules rules, char const * /* name */ = nullptr)
+ {
+ (*this)(static_cast<uint8_t>(rules));
+ }
+
+ void operator()(Timestamp const & t, char const * name = nullptr)
+ {
+ WriteVarUint(m_sink, ToSecondsSinceEpoch(t));
+ }
+
+ template <typename T>
+ void operator()(std::vector<T> const & vs, char const * /* name */ = nullptr)
+ {
+ WriteVarUint(m_sink, static_cast<uint32_t>(vs.size()));
+ for (auto const & v : vs)
+ (*this)(v);
+ }
+
+ template <typename D>
+ std::enable_if_t<std::is_integral<D>::value>
+ operator()(D d, char const * /* name */ = nullptr)
+ {
+ WriteToSink(m_sink, d);
+ }
+
+ template <typename R>
+ std::enable_if_t<!std::is_integral<R>::value>
+ operator()(R const & r, char const * /* name */ = nullptr)
+ {
+ r.Visit(*this);
+ }
+
+ // Skip visiting. It is stored in the separate sections.
+ SKIP_VISITING(LocalizableString const &)
+ SKIP_VISITING(std::string const &)
+ SKIP_VISITING(std::vector<std::string> const &)
+ SKIP_VISITING(std::vector<BookmarkData> const &)
+ SKIP_VISITING(std::vector<TrackData> const &)
+
+private:
+ Sink & m_sink;
+};
+
+template <typename Sink>
+class BookmarkSerializerVisitor
+{
+public:
+ explicit BookmarkSerializerVisitor(Sink & sink)
+ : m_sink(sink)
+ {}
+
+ void operator()(LocalizableStringIndex const & index, char const * /* name */ = nullptr)
+ {
+ WriteLocalizableStringIndex(m_sink, index);
+ }
+
+ void operator()(bool b, char const * /* name */ = nullptr)
+ {
+ (*this)(static_cast<uint8_t>(b));
+ }
+
+ void operator()(m2::PointD const & pt, char const * /* name */ = nullptr)
+ {
+ uint64_t const encoded = bits::ZigZagEncode(PointToInt64(pt, POINT_COORD_BITS));
+ WriteVarUint(m_sink, encoded);
+ }
+
+ void operator()(double d, char const * /* name */ = nullptr)
+ {
+ uint64_t const encoded = DoubleToUint32(d, kMinLineWidth, kMaxLineWidth, 30 /* coordBits */);
+ WriteVarUint(m_sink, encoded);
+ }
+
+ void operator()(Timestamp const & t, char const * name = nullptr)
+ {
+ WriteVarUint(m_sink, ToSecondsSinceEpoch(t));
+ }
+
+ void operator()(PredefinedColor color, char const * /* name */ = nullptr)
+ {
+ (*this)(static_cast<uint8_t>(color));
+ }
+
+ void operator()(BookmarkIcon icon, char const * /* name */ = nullptr)
+ {
+ (*this)(static_cast<uint16_t>(icon));
+ }
+
+ template <typename T>
+ void operator()(std::vector<T> const & vs, char const * /* name */ = nullptr)
+ {
+ WriteVarUint(m_sink, static_cast<uint32_t>(vs.size()));
+ for (auto const & v : vs)
+ (*this)(v);
+ }
+
+ template <typename D>
+ std::enable_if_t<std::is_integral<D>::value>
+ operator()(D d, char const * /* name */ = nullptr)
+ {
+ WriteToSink(m_sink, d);
+ }
+
+ template <typename R>
+ std::enable_if_t<!std::is_integral<R>::value>
+ operator()(R const & r, char const * /* name */ = nullptr)
+ {
+ r.Visit(*this);
+ }
+
+ // Skip visiting. It is stored in the separate sections.
+ SKIP_VISITING(LocalizableString const &)
+ SKIP_VISITING(std::string const &)
+ SKIP_VISITING(std::vector<std::string> const &)
+
+private:
+ Sink & m_sink;
+};
+
+template <typename Source>
+inline void ReadLocalizableStringIndex(Source & source, LocalizableStringIndex & index)
+{
+ auto const indexSize = ReadVarUint<uint32_t, Source>(source);
+ index.reserve(indexSize);
+ for (uint32_t i = 0; i < indexSize; ++i)
+ {
+ index.emplace_back(LocalizableStringSubIndex());
+ auto const subIndexSize = ReadVarUint<uint32_t, Source>(source);
+ for (uint32_t j = 0; j < subIndexSize; ++j)
+ {
+ auto const lang = ReadPrimitiveFromSource<int8_t>(source);
+ auto const strIndex = ReadVarUint<uint32_t, Source>(source);
+ index.back()[lang] = strIndex;
+ }
+ }
+}
+
+template <typename Source>
+class CategoryDeserializerVisitor
+{
+public:
+ explicit CategoryDeserializerVisitor(Source & source)
+ : m_source(source)
+ {}
+
+ void operator()(LocalizableStringIndex & index, char const * /* name */ = nullptr)
+ {
+ ReadLocalizableStringIndex(m_source, index);
+ }
+
+ void operator()(bool & b, char const * /* name */ = nullptr)
+ {
+ b = static_cast<bool>(ReadPrimitiveFromSource<uint8_t>(m_source));
+ }
+
+ void operator()(AccessRules & rules, char const * /* name */ = nullptr)
+ {
+ rules = static_cast<AccessRules>(ReadPrimitiveFromSource<uint8_t>(m_source));
+ }
+
+ void operator()(Timestamp & t, char const * /* name */ = nullptr)
+ {
+ auto const v = ReadVarUint<uint64_t, Source>(m_source);
+ t = FromSecondsSinceEpoch(v);
+ }
+
+ template <typename T>
+ void operator()(std::vector<T> & vs, char const * /* name */ = nullptr)
+ {
+ auto const sz = ReadVarUint<uint32_t, Source>(m_source);
+ vs.reserve(sz);
+ for (uint32_t i = 0; i < sz; ++i)
+ {
+ vs.emplace_back(T());
+ (*this)(vs.back());
+ }
+ }
+
+ template <typename D>
+ std::enable_if_t<std::is_integral<D>::value>
+ operator()(D & d, char const * /* name */ = nullptr)
+ {
+ d = ReadPrimitiveFromSource<D>(m_source);
+ }
+
+ template <typename R>
+ std::enable_if_t<!std::is_integral<R>::value>
+ operator()(R & r, char const * /* name */ = nullptr)
+ {
+ r.Visit(*this);
+ }
+
+ // Skip visiting. It is stored in the separate sections.
+ SKIP_VISITING(LocalizableString &)
+ SKIP_VISITING(std::string &)
+ SKIP_VISITING(std::vector<std::string> &)
+ SKIP_VISITING(std::vector<BookmarkData> &)
+ SKIP_VISITING(std::vector<TrackData> &)
+
+private:
+ Source & m_source;
+};
+
+template <typename Source>
+class BookmarkDeserializerVisitor
+{
+public:
+ explicit BookmarkDeserializerVisitor(Source & source)
+ : m_source(source)
+ {}
+
+ void operator()(LocalizableStringIndex & index, char const * /* name */ = nullptr)
+ {
+ ReadLocalizableStringIndex(m_source, index);
+ }
+
+ void operator()(bool & b, char const * /* name */ = nullptr)
+ {
+ b = static_cast<bool>(ReadPrimitiveFromSource<uint8_t>(m_source));
+ }
+
+ void operator()(m2::PointD & pt, char const * /* name */ = nullptr)
+ {
+ auto const v = ReadVarUint<uint64_t, Source>(m_source);
+ pt = Int64ToPoint(bits::ZigZagDecode(v), POINT_COORD_BITS);
+ }
+
+ void operator()(double & d, char const * /* name */ = nullptr)
+ {
+ auto const v = ReadVarUint<uint32_t, Source>(m_source);
+ d = Uint32ToDouble(v, kMinLineWidth, kMaxLineWidth, 30 /* coordBits */);
+ }
+
+ void operator()(Timestamp & t, char const * /* name */ = nullptr)
+ {
+ auto const v = ReadVarUint<uint64_t, Source>(m_source);
+ t = FromSecondsSinceEpoch(v);
+ }
+
+ void operator()(PredefinedColor & color, char const * /* name */ = nullptr)
+ {
+ color = static_cast<PredefinedColor>(ReadPrimitiveFromSource<uint8_t>(m_source));
+ }
+
+ void operator()(AccessRules & rules, char const * /* name */ = nullptr)
+ {
+ rules = static_cast<AccessRules>(ReadPrimitiveFromSource<uint8_t>(m_source));
+ }
+
+ void operator()(BookmarkIcon & icon, char const * /* name */ = nullptr)
+ {
+ icon = static_cast<BookmarkIcon>(ReadPrimitiveFromSource<uint16_t>(m_source));
+ }
+
+ template <typename T>
+ void operator()(std::vector<T> & vs, char const * /* name */ = nullptr)
+ {
+ auto const sz = ReadVarUint<uint32_t, Source>(m_source);
+ vs.reserve(sz);
+ for (uint32_t i = 0; i < sz; ++i)
+ {
+ vs.emplace_back(T());
+ (*this)(vs.back());
+ }
+ }
+
+ template <typename D>
+ std::enable_if_t<std::is_integral<D>::value>
+ operator()(D & d, char const * /* name */ = nullptr)
+ {
+ d = ReadPrimitiveFromSource<D>(m_source);
+ }
+
+ template <typename R>
+ std::enable_if_t<!std::is_integral<R>::value>
+ operator()(R & r, char const * /* name */ = nullptr)
+ {
+ r.Visit(*this);
+ }
+
+ // Skip visiting. It is stored in the separate sections.
+ SKIP_VISITING(LocalizableString &)
+ SKIP_VISITING(std::string &)
+ SKIP_VISITING(std::vector<std::string> &)
+
+private:
+ Source & m_source;
+};
+
+template <typename Reader>
+class DeserializedStringCollector
+{
+public:
+ explicit DeserializedStringCollector(coding::BlockedTextStorage<Reader> & textStorage)
+ : m_textStorage(textStorage)
+ {}
+
+ template <typename... OtherStrings>
+ void Collect(LocalizableStringIndex & index, LocalizableString & str,
+ OtherStrings & ... args)
+ {
+ if (!SwitchSubIndexIfNeeded(index))
+ return;
+
+ auto subIndex = index[m_counter];
+ for (auto const & p : subIndex)
+ str[p.first] = ExtractString(subIndex, p.second);
+
+ m_counter++;
+ Collect(index, args...);
+ }
+
+ template <typename... OtherStrings>
+ void Collect(LocalizableStringIndex & index, std::string & str,
+ OtherStrings & ... args)
+ {
+ if (!SwitchSubIndexIfNeeded(index))
+ return;
+
+ auto subIndex = index[m_counter];
+ if (!subIndex.empty())
+ str = ExtractString(subIndex, subIndex.begin()->second);
+ else
+ str = {};
+
+ m_counter++;
+ Collect(index, args...);
+ }
+
+ template <typename... OtherStrings>
+ void Collect(LocalizableStringIndex & index, std::vector<std::string> & stringsArray,
+ OtherStrings & ... args)
+ {
+ if (!SwitchSubIndexIfNeeded(index))
+ return;
+
+ auto subIndex = index[m_counter];
+ stringsArray.reserve(subIndex.size());
+ for (auto const & p : subIndex)
+ stringsArray.emplace_back(ExtractString(subIndex, p.second));
+
+ m_counter++;
+ Collect(index, args...);
+ }
+
+ template <typename...>
+ void Collect(LocalizableStringIndex & index) {}
+
+private:
+ bool SwitchSubIndexIfNeeded(LocalizableStringIndex & index)
+ {
+ if (m_lastIndex != &index)
+ {
+ m_counter = 0;
+ m_lastIndex = &index;
+ }
+ return m_counter < index.size();
+ }
+
+ std::string ExtractString(LocalizableStringSubIndex const & subIndex,
+ uint32_t stringIndex) const
+ {
+ auto const stringsCount = m_textStorage.GetNumStrings();
+ if (stringIndex >= stringsCount)
+ return {};
+
+ return m_textStorage.ExtractString(stringIndex);
+ }
+
+ coding::BlockedTextStorage<Reader> & m_textStorage;
+ LocalizableStringIndex * m_lastIndex = nullptr;
+ size_t m_counter = 0;
+};
+} // namespace binary
+} // namespace kml
diff --git a/map/benchmark_tool/CMakeLists.txt b/map/benchmark_tool/CMakeLists.txt
index 223bf562cb..4cc0c10254 100644
--- a/map/benchmark_tool/CMakeLists.txt
+++ b/map/benchmark_tool/CMakeLists.txt
@@ -22,6 +22,7 @@ omim_link_libraries(
indexer
editor
geometry
+ kml
platform
coding
base
diff --git a/map/cloud.cpp b/map/cloud.cpp
index ff6c5b8516..80ab3c1aec 100644
--- a/map/cloud.cpp
+++ b/map/cloud.cpp
@@ -4,6 +4,7 @@
#include "coding/file_reader.hpp"
#include "coding/file_writer.hpp"
#include "coding/internal/file_data.hpp"
+#include "coding/sha1.hpp"
#include "coding/zip_creator.hpp"
#include "platform/network_policy.hpp"
@@ -20,9 +21,6 @@
#include <chrono>
#include <sstream>
-#include "3party/liboauthcpp/src/SHA1.h"
-#include "3party/liboauthcpp/src/base64.h"
-
//#define STAGE_CLOUD_SERVER
#include "private.h"
@@ -77,34 +75,6 @@ std::string ExtractFileNameWithoutExtension(std::string const & filePath)
return path;
}
-std::string CalculateSHA1(std::string const & filePath)
-{
- uint32_t constexpr kFileBufferSize = 8192;
- try
- {
- my::FileData file(filePath, my::FileData::OP_READ);
- uint64_t const fileSize = file.Size();
-
- CSHA1 sha1;
- uint64_t currSize = 0;
- unsigned char buffer[kFileBufferSize];
- while (currSize < fileSize)
- {
- auto const toRead = std::min(kFileBufferSize, static_cast<uint32_t>(fileSize - currSize));
- file.Read(currSize, buffer, toRead);
- sha1.Update(buffer, toRead);
- currSize += toRead;
- }
- sha1.Final();
- return base64_encode(sha1.m_digest, ARRAY_SIZE(sha1.m_digest));
- }
- catch (Reader::Exception const & ex)
- {
- LOG(LERROR, ("Error reading file:", filePath, ex.Msg()));
- }
- return {};
-}
-
template<typename DataType>
std::string SerializeToJson(DataType const & data)
{
@@ -483,7 +453,7 @@ void Cloud::ScheduleUploadingTask(EntryPtr const & entry, uint32_t timeout,
}
// Upload only if SHA1 is not equal to previous one.
- auto const sha1 = CalculateSHA1(uploadedName);
+ auto const sha1 = coding::SHA1::CalculateBase64(uploadedName);
if (sha1.empty())
{
FinishUploading(SynchronizationResult::DiskError, "SHA1 calculation error");
@@ -620,7 +590,7 @@ std::string Cloud::PrepareFileToUploading(std::string const & fileName)
}
// 2. Calculate SHA1 of the original uploading file.
- auto const originalSha1 = CalculateSHA1(filePath);
+ auto const originalSha1 = coding::SHA1::CalculateBase64(filePath);
if (originalSha1.empty())
return {};
@@ -635,7 +605,7 @@ std::string Cloud::PrepareFileToUploading(std::string const & fileName)
// 4. Calculate SHA1 of the temporary file and compare with original one.
// Original file can be modified during copying process, so we have to
// compare original file with the temporary file after copying.
- auto const tmpSha1 = CalculateSHA1(tmpPath);
+ auto const tmpSha1 = coding::SHA1::CalculateBase64(tmpPath);
if (originalSha1 != tmpSha1)
return {};
diff --git a/map/map_tests/CMakeLists.txt b/map/map_tests/CMakeLists.txt
index 6c66b0322b..c609dd6368 100644
--- a/map/map_tests/CMakeLists.txt
+++ b/map/map_tests/CMakeLists.txt
@@ -45,6 +45,7 @@ omim_link_libraries(
indexer
partners_api
local_ads
+ kml
platform
editor
mwm_diff
diff --git a/map/mwm_tests/CMakeLists.txt b/map/mwm_tests/CMakeLists.txt
index ddf4a3931e..aa7ddd6b3a 100644
--- a/map/mwm_tests/CMakeLists.txt
+++ b/map/mwm_tests/CMakeLists.txt
@@ -15,6 +15,7 @@ omim_link_libraries(
storage
indexer
editor
+ kml
platform
geometry
coding
diff --git a/map/style_tests/CMakeLists.txt b/map/style_tests/CMakeLists.txt
index 4b3e6054b6..d10a768137 100644
--- a/map/style_tests/CMakeLists.txt
+++ b/map/style_tests/CMakeLists.txt
@@ -27,6 +27,7 @@ omim_link_libraries(
opening_hours
editor
oauthcpp
+ kml
platform
jansson
geometry
diff --git a/mapshot/CMakeLists.txt b/mapshot/CMakeLists.txt
index 55c2eefaaa..06349fd13b 100644
--- a/mapshot/CMakeLists.txt
+++ b/mapshot/CMakeLists.txt
@@ -29,6 +29,7 @@ omim_link_libraries(
drape
partners_api
local_ads
+ kml
platform
editor
geometry
diff --git a/openlr/openlr_match_quality/openlr_assessment_tool/CMakeLists.txt b/openlr/openlr_match_quality/openlr_assessment_tool/CMakeLists.txt
index 2c184c6786..5927fcbacf 100644
--- a/openlr/openlr_match_quality/openlr_assessment_tool/CMakeLists.txt
+++ b/openlr/openlr_match_quality/openlr_assessment_tool/CMakeLists.txt
@@ -48,6 +48,7 @@ omim_link_libraries(
drape
partners_api
local_ads
+ kml
platform
editor
mwm_diff
diff --git a/qt/CMakeLists.txt b/qt/CMakeLists.txt
index faeead578a..594027f3c6 100644
--- a/qt/CMakeLists.txt
+++ b/qt/CMakeLists.txt
@@ -73,6 +73,7 @@ omim_link_libraries(
drape
partners_api
local_ads
+ kml
platform
editor
geometry
diff --git a/routing/routing_benchmarks/CMakeLists.txt b/routing/routing_benchmarks/CMakeLists.txt
index 2fcf70dac2..233b2ab2cf 100644
--- a/routing/routing_benchmarks/CMakeLists.txt
+++ b/routing/routing_benchmarks/CMakeLists.txt
@@ -24,6 +24,7 @@ omim_link_libraries(
mwm_diff
indexer
traffic
+ kml
platform
editor
oauthcpp
diff --git a/routing/routing_consistency_tests/CMakeLists.txt b/routing/routing_consistency_tests/CMakeLists.txt
index 6e9236f55a..304247f0db 100644
--- a/routing/routing_consistency_tests/CMakeLists.txt
+++ b/routing/routing_consistency_tests/CMakeLists.txt
@@ -25,6 +25,7 @@ omim_link_libraries(
storage
mwm_diff
indexer
+ kml
platform
editor
geometry
diff --git a/routing/routing_integration_tests/CMakeLists.txt b/routing/routing_integration_tests/CMakeLists.txt
index 0d0d2505e1..0d656f8bd7 100644
--- a/routing/routing_integration_tests/CMakeLists.txt
+++ b/routing/routing_integration_tests/CMakeLists.txt
@@ -36,6 +36,7 @@ omim_link_libraries(
traffic
routing_common
transit
+ kml
platform
oauthcpp
geometry
diff --git a/routing/routing_tests/CMakeLists.txt b/routing/routing_tests/CMakeLists.txt
index aa3d69e11e..5a1f98e9ab 100644
--- a/routing/routing_tests/CMakeLists.txt
+++ b/routing/routing_tests/CMakeLists.txt
@@ -42,6 +42,7 @@ omim_link_libraries(
traffic
routing_common
transit
+ kml
platform
oauthcpp
geometry
diff --git a/search/search_quality/assessment_tool/CMakeLists.txt b/search/search_quality/assessment_tool/CMakeLists.txt
index 821a3df09a..d74df0dabe 100644
--- a/search/search_quality/assessment_tool/CMakeLists.txt
+++ b/search/search_quality/assessment_tool/CMakeLists.txt
@@ -53,6 +53,7 @@ omim_link_libraries(
drape
partners_api
local_ads
+ kml
platform
editor
mwm_diff
diff --git a/storage/storage_integration_tests/CMakeLists.txt b/storage/storage_integration_tests/CMakeLists.txt
index 3fe9065274..20308097ab 100644
--- a/storage/storage_integration_tests/CMakeLists.txt
+++ b/storage/storage_integration_tests/CMakeLists.txt
@@ -33,6 +33,7 @@ omim_link_libraries(
drape
partners_api
local_ads
+ kml
platform_tests_support
platform
editor
diff --git a/storage/storage_tests/CMakeLists.txt b/storage/storage_tests/CMakeLists.txt
index 7913f7e5dd..b20188621c 100644
--- a/storage/storage_tests/CMakeLists.txt
+++ b/storage/storage_tests/CMakeLists.txt
@@ -38,6 +38,7 @@ omim_link_libraries(
platform_tests_support
editor
oauthcpp
+ kml
platform
opening_hours
mwm_diff
diff --git a/track_analyzing/track_analyzer/CMakeLists.txt b/track_analyzing/track_analyzer/CMakeLists.txt
index f2c57a2f2c..3e63daf882 100644
--- a/track_analyzing/track_analyzer/CMakeLists.txt
+++ b/track_analyzing/track_analyzer/CMakeLists.txt
@@ -25,6 +25,7 @@ omim_link_libraries(
storage
indexer
editor
+ kml
platform
mwm_diff
bsdiff
diff --git a/ugc/binary/header_v0.hpp b/ugc/binary/header_v0.hpp
index be5846d2a3..3c3914693f 100644
--- a/ugc/binary/header_v0.hpp
+++ b/ugc/binary/header_v0.hpp
@@ -1,6 +1,7 @@
#pragma once
#include "coding/reader.hpp"
+#include "coding/serdes_binary_header.hpp"
#include "coding/write_to_sink.hpp"
#include <cstdint>
@@ -9,57 +10,6 @@ namespace ugc
{
namespace binary
{
-namespace impl
-{
-struct HeaderSizeOfVisitor
-{
- void operator()(uint64_t v, char const * /* name */ = nullptr) { m_size += sizeof(v); }
-
- template <typename R>
- void operator()(R & r, char const * /* name */ = nullptr)
- {
- r.Visit(*this);
- }
-
- uint64_t m_size = 0;
-};
-
-template <typename Sink>
-struct HeaderSerVisitor
-{
- HeaderSerVisitor(Sink & sink) : m_sink(sink) {}
-
- void operator()(uint64_t v, char const * /* name */ = nullptr) { WriteToSink(m_sink, v); }
-
- template <typename R>
- void operator()(R & r, char const * /* name */ = nullptr)
- {
- r.Visit(*this);
- }
-
- Sink & m_sink;
-};
-
-template <typename Source>
-struct HeaderDesVisitor
-{
- HeaderDesVisitor(Source & source): m_source(source) {}
-
- void operator()(uint64_t & v, char const * /* name */ = nullptr)
- {
- v = ReadPrimitiveFromSource<uint64_t>(m_source);
- }
-
- template <typename R>
- void operator()(R & r, char const * /* name */ = nullptr)
- {
- r.Visit(*this);
- }
-
- Source & m_source;
-};
-} // namespace impl
-
struct HeaderV0
{
template <typename Visitor>
@@ -75,21 +25,21 @@ struct HeaderV0
template <typename Sink>
void Serialize(Sink & sink)
{
- impl::HeaderSerVisitor<Sink> visitor(sink);
+ coding::binary::HeaderSerVisitor<Sink> visitor(sink);
visitor(*this);
}
template <typename Source>
void Deserialize(Source & source)
{
- impl::HeaderDesVisitor<Source> visitor(source);
+ coding::binary::HeaderDesVisitor<Source> visitor(source);
visitor(*this);
}
// Calculates the size of serialized header in bytes.
uint64_t Size()
{
- impl::HeaderSizeOfVisitor visitor;
+ coding::binary::HeaderSizeOfVisitor visitor;
visitor(*this);
return visitor.m_size;
}
diff --git a/xcode/coding/coding.xcodeproj/project.pbxproj b/xcode/coding/coding.xcodeproj/project.pbxproj
index ba6f8e8ad6..14fa59d783 100644
--- a/xcode/coding/coding.xcodeproj/project.pbxproj
+++ b/xcode/coding/coding.xcodeproj/project.pbxproj
@@ -33,6 +33,9 @@
3D74EF211F8F55740081202C /* csv_reader.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 3D74EF1F1F8F55740081202C /* csv_reader.hpp */; };
3D74EF221F8F55740081202C /* csv_reader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3D74EF201F8F55740081202C /* csv_reader.cpp */; };
454523B4202AEB21009275C1 /* serdes_json.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 454523B3202AEB21009275C1 /* serdes_json.hpp */; };
+ 4563B061205909290057556D /* serdes_binary_header.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 4563B05E205909280057556D /* serdes_binary_header.hpp */; };
+ 4563B062205909290057556D /* sha1.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4563B05F205909280057556D /* sha1.cpp */; };
+ 4563B063205909290057556D /* sha1.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 4563B060205909280057556D /* sha1.hpp */; };
45C108B41E9CFE69000FE1F6 /* point_to_integer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 45C108B21E9CFE69000FE1F6 /* point_to_integer.cpp */; };
45C108B51E9CFE69000FE1F6 /* point_to_integer.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 45C108B31E9CFE69000FE1F6 /* point_to_integer.hpp */; };
45C108B81E9CFE7B000FE1F6 /* point_to_integer_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 45C108B61E9CFE78000FE1F6 /* point_to_integer_test.cpp */; };
@@ -185,6 +188,9 @@
3D74EF1F1F8F55740081202C /* csv_reader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = csv_reader.hpp; sourceTree = "<group>"; };
3D74EF201F8F55740081202C /* csv_reader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = csv_reader.cpp; sourceTree = "<group>"; };
454523B3202AEB21009275C1 /* serdes_json.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = serdes_json.hpp; sourceTree = "<group>"; };
+ 4563B05E205909280057556D /* serdes_binary_header.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = serdes_binary_header.hpp; sourceTree = "<group>"; };
+ 4563B05F205909280057556D /* sha1.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = sha1.cpp; sourceTree = "<group>"; };
+ 4563B060205909280057556D /* sha1.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = sha1.hpp; sourceTree = "<group>"; };
45C108B21E9CFE69000FE1F6 /* point_to_integer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = point_to_integer.cpp; sourceTree = "<group>"; };
45C108B31E9CFE69000FE1F6 /* point_to_integer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = point_to_integer.hpp; sourceTree = "<group>"; };
45C108B61E9CFE78000FE1F6 /* point_to_integer_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = point_to_integer_test.cpp; sourceTree = "<group>"; };
@@ -470,7 +476,10 @@
675342641A3F588B00A0A8C3 /* reader_writer_ops.hpp */,
675342651A3F588B00A0A8C3 /* reader.cpp */,
675342661A3F588B00A0A8C3 /* reader.hpp */,
+ 4563B05E205909280057556D /* serdes_binary_header.hpp */,
454523B3202AEB21009275C1 /* serdes_json.hpp */,
+ 4563B05F205909280057556D /* sha1.cpp */,
+ 4563B060205909280057556D /* sha1.hpp */,
347F33341C4540F0009758CC /* simple_dense_coding.cpp */,
347F33351C4540F0009758CC /* simple_dense_coding.hpp */,
675342691A3F588B00A0A8C3 /* streams_common.hpp */,
@@ -540,6 +549,7 @@
675342B21A3F588C00A0A8C3 /* parse_xml.hpp in Headers */,
675342AF1A3F588C00A0A8C3 /* mmap_reader.hpp in Headers */,
675342901A3F588C00A0A8C3 /* coder.hpp in Headers */,
+ 4563B061205909290057556D /* serdes_binary_header.hpp in Headers */,
675342B81A3F588C00A0A8C3 /* reader_wrapper.hpp in Headers */,
675342AD1A3F588C00A0A8C3 /* matrix_traversal.hpp in Headers */,
675342CB1A3F588C00A0A8C3 /* varint_misc.hpp in Headers */,
@@ -556,6 +566,7 @@
675342B41A3F588C00A0A8C3 /* read_write_utils.hpp in Headers */,
675342981A3F588C00A0A8C3 /* diff.hpp in Headers */,
670D04C01B0BA92D0013A7AC /* file_data.hpp in Headers */,
+ 4563B063205909290057556D /* sha1.hpp in Headers */,
6753428B1A3F588C00A0A8C3 /* buffer_reader.hpp in Headers */,
675342831A3F588C00A0A8C3 /* base64.hpp in Headers */,
675342BA1A3F588C00A0A8C3 /* reader_writer_ops.hpp in Headers */,
@@ -715,6 +726,7 @@
675342B01A3F588C00A0A8C3 /* multilang_utf8_string.cpp in Sources */,
675342821A3F588C00A0A8C3 /* base64.cpp in Sources */,
675342D31A3F588C00A0A8C3 /* zip_reader.cpp in Sources */,
+ 4563B062205909290057556D /* sha1.cpp in Sources */,
347F33371C4540F0009758CC /* compressed_bit_vector.cpp in Sources */,
3D74EF221F8F55740081202C /* csv_reader.cpp in Sources */,
6753429F1A3F588C00A0A8C3 /* file_reader.cpp in Sources */,
diff --git a/xcode/kml/kml.xcodeproj/project.pbxproj b/xcode/kml/kml.xcodeproj/project.pbxproj
new file mode 100644
index 0000000000..0ee994f90d
--- /dev/null
+++ b/xcode/kml/kml.xcodeproj/project.pbxproj
@@ -0,0 +1,349 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 48;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 45E4559520584ABA00D9F45E /* serdes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 45E4558E20584AB900D9F45E /* serdes.cpp */; };
+ 45E4559620584ABA00D9F45E /* serdes_binary.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 45E4559220584ABA00D9F45E /* serdes_binary.cpp */; };
+ 45E4560120584DF200D9F45E /* serdes_tests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 45E455FF20584DEF00D9F45E /* serdes_tests.cpp */; };
+ 45E4560320584E1C00D9F45E /* libkml.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 45E4557D205849A600D9F45E /* libkml.a */; };
+ 45E4560420584E2600D9F45E /* libplatform.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 45E4560520584E2600D9F45E /* libplatform.a */; };
+ 45E4560620584E3600D9F45E /* libcoding.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 45E4560720584E3600D9F45E /* libcoding.a */; };
+ 45E4560820584E4100D9F45E /* libgeometry.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 45E4560920584E4100D9F45E /* libgeometry.a */; };
+ 45E4560A20584E4700D9F45E /* libbase.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 45E4560B20584E4700D9F45E /* libbase.a */; };
+ 45E4560C20584E5200D9F45E /* libexpat.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 45E4560D20584E5200D9F45E /* libexpat.a */; };
+ 45E4560E20584E6A00D9F45E /* libalohalitics.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 45E4560F20584E6A00D9F45E /* libalohalitics.a */; };
+ 45E4561120584E8F00D9F45E /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 45E4561020584E8F00D9F45E /* libz.tbd */; };
+ 45E456142058509200D9F45E /* testingmain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 45E456122058508C00D9F45E /* testingmain.cpp */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 45E4557B205849A600D9F45E /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "include/$(PRODUCT_NAME)";
+ dstSubfolderSpec = 16;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 45E4557D205849A600D9F45E /* libkml.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libkml.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ 45E45589205849DC00D9F45E /* common-release.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = "common-release.xcconfig"; path = "../common-release.xcconfig"; sourceTree = "<group>"; };
+ 45E4558A205849DC00D9F45E /* common-debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = "common-debug.xcconfig"; path = "../common-debug.xcconfig"; sourceTree = "<group>"; };
+ 45E4558D20584AB900D9F45E /* header_binary.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = header_binary.hpp; sourceTree = "<group>"; };
+ 45E4558E20584AB900D9F45E /* serdes.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = serdes.cpp; sourceTree = "<group>"; };
+ 45E4558F20584AB900D9F45E /* type_utils.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = type_utils.hpp; sourceTree = "<group>"; };
+ 45E4559020584AB900D9F45E /* serdes.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = serdes.hpp; sourceTree = "<group>"; };
+ 45E4559120584ABA00D9F45E /* visitors.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = visitors.hpp; sourceTree = "<group>"; };
+ 45E4559220584ABA00D9F45E /* serdes_binary.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = serdes_binary.cpp; sourceTree = "<group>"; };
+ 45E4559320584ABA00D9F45E /* serdes_binary.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = serdes_binary.hpp; sourceTree = "<group>"; };
+ 45E4559420584ABA00D9F45E /* types.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = types.hpp; sourceTree = "<group>"; };
+ 45E455ED20584DCB00D9F45E /* kml_tests.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = kml_tests.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 45E455FF20584DEF00D9F45E /* serdes_tests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = serdes_tests.cpp; sourceTree = "<group>"; };
+ 45E4560520584E2600D9F45E /* libplatform.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libplatform.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ 45E4560720584E3600D9F45E /* libcoding.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libcoding.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ 45E4560920584E4100D9F45E /* libgeometry.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libgeometry.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ 45E4560B20584E4700D9F45E /* libbase.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libbase.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ 45E4560D20584E5200D9F45E /* libexpat.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libexpat.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ 45E4560F20584E6A00D9F45E /* libalohalitics.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libalohalitics.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ 45E4561020584E8F00D9F45E /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
+ 45E456122058508C00D9F45E /* testingmain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testingmain.cpp; path = ../../testing/testingmain.cpp; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 45E4557A205849A600D9F45E /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 45E455EA20584DCB00D9F45E /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 45E4560320584E1C00D9F45E /* libkml.a in Frameworks */,
+ 45E4560420584E2600D9F45E /* libplatform.a in Frameworks */,
+ 45E4560620584E3600D9F45E /* libcoding.a in Frameworks */,
+ 45E4560820584E4100D9F45E /* libgeometry.a in Frameworks */,
+ 45E4560A20584E4700D9F45E /* libbase.a in Frameworks */,
+ 45E4560C20584E5200D9F45E /* libexpat.a in Frameworks */,
+ 45E4560E20584E6A00D9F45E /* libalohalitics.a in Frameworks */,
+ 45E4561120584E8F00D9F45E /* libz.tbd in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 45E45574205849A600D9F45E = {
+ isa = PBXGroup;
+ children = (
+ 45E4558A205849DC00D9F45E /* common-debug.xcconfig */,
+ 45E45589205849DC00D9F45E /* common-release.xcconfig */,
+ 45E4557F205849A600D9F45E /* kml */,
+ 45E455EE20584DCC00D9F45E /* kml_tests */,
+ 45E4557E205849A600D9F45E /* Products */,
+ 45E4560220584E1C00D9F45E /* Frameworks */,
+ );
+ sourceTree = "<group>";
+ };
+ 45E4557E205849A600D9F45E /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 45E4557D205849A600D9F45E /* libkml.a */,
+ 45E455ED20584DCB00D9F45E /* kml_tests.app */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 45E4557F205849A600D9F45E /* kml */ = {
+ isa = PBXGroup;
+ children = (
+ 45E4558D20584AB900D9F45E /* header_binary.hpp */,
+ 45E4559220584ABA00D9F45E /* serdes_binary.cpp */,
+ 45E4559320584ABA00D9F45E /* serdes_binary.hpp */,
+ 45E4558E20584AB900D9F45E /* serdes.cpp */,
+ 45E4559020584AB900D9F45E /* serdes.hpp */,
+ 45E4558F20584AB900D9F45E /* type_utils.hpp */,
+ 45E4559420584ABA00D9F45E /* types.hpp */,
+ 45E4559120584ABA00D9F45E /* visitors.hpp */,
+ );
+ name = kml;
+ path = ../../kml;
+ sourceTree = "<group>";
+ };
+ 45E455EE20584DCC00D9F45E /* kml_tests */ = {
+ isa = PBXGroup;
+ children = (
+ 45E456122058508C00D9F45E /* testingmain.cpp */,
+ 45E455FF20584DEF00D9F45E /* serdes_tests.cpp */,
+ );
+ name = kml_tests;
+ path = ../../kml/kml_tests;
+ sourceTree = "<group>";
+ };
+ 45E4560220584E1C00D9F45E /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 45E4561020584E8F00D9F45E /* libz.tbd */,
+ 45E4560F20584E6A00D9F45E /* libalohalitics.a */,
+ 45E4560D20584E5200D9F45E /* libexpat.a */,
+ 45E4560B20584E4700D9F45E /* libbase.a */,
+ 45E4560920584E4100D9F45E /* libgeometry.a */,
+ 45E4560720584E3600D9F45E /* libcoding.a */,
+ 45E4560520584E2600D9F45E /* libplatform.a */,
+ );
+ name = Frameworks;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 45E4557C205849A600D9F45E /* kml */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 45E45586205849A600D9F45E /* Build configuration list for PBXNativeTarget "kml" */;
+ buildPhases = (
+ 45E45579205849A600D9F45E /* Sources */,
+ 45E4557A205849A600D9F45E /* Frameworks */,
+ 45E4557B205849A600D9F45E /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = kml;
+ productName = kml;
+ productReference = 45E4557D205849A600D9F45E /* libkml.a */;
+ productType = "com.apple.product-type.library.static";
+ };
+ 45E455EC20584DCB00D9F45E /* kml_tests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 45E455FB20584DCC00D9F45E /* Build configuration list for PBXNativeTarget "kml_tests" */;
+ buildPhases = (
+ 45E455E920584DCB00D9F45E /* Sources */,
+ 45E455EA20584DCB00D9F45E /* Frameworks */,
+ 45E455EB20584DCB00D9F45E /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = kml_tests;
+ productName = kml_tests;
+ productReference = 45E455ED20584DCB00D9F45E /* kml_tests.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 45E45575205849A600D9F45E /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 0920;
+ ORGANIZATIONNAME = maps.me;
+ TargetAttributes = {
+ 45E4557C205849A600D9F45E = {
+ CreatedOnToolsVersion = 9.2;
+ ProvisioningStyle = Automatic;
+ };
+ 45E455EC20584DCB00D9F45E = {
+ CreatedOnToolsVersion = 9.2;
+ };
+ };
+ };
+ buildConfigurationList = 45E45578205849A600D9F45E /* Build configuration list for PBXProject "kml" */;
+ compatibilityVersion = "Xcode 8.0";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 45E45574205849A600D9F45E;
+ productRefGroup = 45E4557E205849A600D9F45E /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 45E4557C205849A600D9F45E /* kml */,
+ 45E455EC20584DCB00D9F45E /* kml_tests */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 45E455EB20584DCB00D9F45E /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 45E45579205849A600D9F45E /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 45E4559620584ABA00D9F45E /* serdes_binary.cpp in Sources */,
+ 45E4559520584ABA00D9F45E /* serdes.cpp in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 45E455E920584DCB00D9F45E /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 45E4560120584DF200D9F45E /* serdes_tests.cpp in Sources */,
+ 45E456142058509200D9F45E /* testingmain.cpp in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+ 45E45584205849A600D9F45E /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 45E4558A205849DC00D9F45E /* common-debug.xcconfig */;
+ buildSettings = {
+ };
+ name = Debug;
+ };
+ 45E45585205849A600D9F45E /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 45E45589205849DC00D9F45E /* common-release.xcconfig */;
+ buildSettings = {
+ };
+ name = Release;
+ };
+ 45E45587205849A600D9F45E /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ };
+ name = Debug;
+ };
+ 45E45588205849A600D9F45E /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ };
+ name = Release;
+ };
+ 45E4558B205849F400D9F45E /* Production Full */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 45E45589205849DC00D9F45E /* common-release.xcconfig */;
+ buildSettings = {
+ };
+ name = "Production Full";
+ };
+ 45E4558C205849F400D9F45E /* Production Full */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ };
+ name = "Production Full";
+ };
+ 45E455FC20584DCC00D9F45E /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ INFOPLIST_FILE = "$(OMIM_ROOT)/iphone/Maps/MAPSME.plist";
+ };
+ name = Debug;
+ };
+ 45E455FD20584DCC00D9F45E /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ INFOPLIST_FILE = "$(OMIM_ROOT)/iphone/Maps/MAPSME.plist";
+ };
+ name = Release;
+ };
+ 45E455FE20584DCC00D9F45E /* Production Full */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ INFOPLIST_FILE = "$(OMIM_ROOT)/iphone/Maps/MAPSME.plist";
+ };
+ name = "Production Full";
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 45E45578205849A600D9F45E /* Build configuration list for PBXProject "kml" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 45E45584205849A600D9F45E /* Debug */,
+ 45E45585205849A600D9F45E /* Release */,
+ 45E4558B205849F400D9F45E /* Production Full */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 45E45586205849A600D9F45E /* Build configuration list for PBXNativeTarget "kml" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 45E45587205849A600D9F45E /* Debug */,
+ 45E45588205849A600D9F45E /* Release */,
+ 45E4558C205849F400D9F45E /* Production Full */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 45E455FB20584DCC00D9F45E /* Build configuration list for PBXNativeTarget "kml_tests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 45E455FC20584DCC00D9F45E /* Debug */,
+ 45E455FD20584DCC00D9F45E /* Release */,
+ 45E455FE20584DCC00D9F45E /* Production Full */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 45E45575205849A600D9F45E /* Project object */;
+}
diff --git a/xcode/kml/kml.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/xcode/kml/kml.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000000..b98941d3e9
--- /dev/null
+++ b/xcode/kml/kml.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+ version = "1.0">
+ <FileRef
+ location = "self:kml.xcodeproj">
+ </FileRef>
+</Workspace>
diff --git a/xcode/omim.xcworkspace/contents.xcworkspacedata b/xcode/omim.xcworkspace/contents.xcworkspacedata
index df232cb6f4..e28b780d72 100644
--- a/xcode/omim.xcworkspace/contents.xcworkspacedata
+++ b/xcode/omim.xcworkspace/contents.xcworkspacedata
@@ -323,6 +323,9 @@
location = "container:software_renderer/software_renderer.xcodeproj">
</FileRef>
<FileRef
+ location = "container:kml/kml.xcodeproj">
+ </FileRef>
+ <FileRef
location = "container:../iphone/Maps/Maps.xcodeproj">
</FileRef>
</Workspace>