diff --git a/generator/generator_tests/osm_o5m_source_test.cpp b/generator/generator_tests/osm_o5m_source_test.cpp
new file mode 100644
index 0000000000..ab9458a0dc
--- /dev/null
+++ b/generator/generator_tests/osm_o5m_source_test.cpp
@@ -0,0 +1,342 @@
+//
+// osm_o5m_source_test.cpp
+// generator_tool
+//
+// Created by Sergey Yershov on 14.04.15.
+//
+
+#include "testing/testing.hpp"
+#include "generator/osm_o5m_source.hpp"
+
+#include "std/set.hpp"
+
+
+/*
+
+
+
+
+
+
+
+
+
+
+ */
+
+/* begin binary data: node.o5m */
+uint8_t node_o5m_data[] = /* 92 */
+{0xFF,0xE0,0x04,0x6F,0x35,0x6D,0x32,0xFF,0x10,0x51,0xA1,0xAB,0x21,0x00,0xCD
+ ,0xE6,0xD7,0x80,0x0A,0xBE,0xCE,0x82,0xD1,0x04,0x00,0x6E,0x61,0x6D,0x65,0x00
+ ,0xD0,0x9F,0xD1,0x80,0xD0,0xBE,0xD0,0xB4,0xD1,0x83,0xD0,0xBA,0xD1,0x82,0xD0
+ ,0xBE,0xD0,0xB2,0xD1,0x8B,0xD0,0xB9,0x00,0x00,0x6F,0x70,0x65,0x6E,0x69,0x6E
+ ,0x67,0x5F,0x68,0x6F,0x75,0x72,0x73,0x00,0x32,0x34,0x2F,0x37,0x00,0x00,0x73
+ ,0x68,0x6F,0x70,0x00,0x63,0x6F,0x6E,0x76,0x65,0x6E,0x69,0x65,0x6E,0x63,0x65
+ ,0x00,0xFE};
+/* end binary data. size = 92 bytes */
+
+
+/*
+
+
+
+
+
+
+
+
+ */
+
+/* begin binary data: node2.o5m */
+uint8_t node2_o5m_data[] = /* 93 */
+{0xFF,0xE0,0x04,0x6F,0x35,0x6D,0x32,0xFF,0x10,0x52,0x94,0xDD,0xF4,0xE9,0x03
+ ,0x08,0xD6,0xBD,0xEF,0xFE,0x09,0xF0,0x87,0xC0,0x0B,0x00,0xBF,0x8E,0x18,0x00
+ ,0x58,0x6D,0x79,0x70,0x62,0x6C,0x75,0x00,0xA0,0xCC,0xDA,0xF1,0x02,0xAC,0xEB
+ ,0xB3,0x8D,0x04,0x00,0x61,0x6D,0x65,0x6E,0x69,0x74,0x79,0x00,0x63,0x69,0x6E
+ ,0x65,0x6D,0x61,0x00,0x00,0x6E,0x61,0x6D,0x65,0x00,0xD0,0x9A,0xD0,0xA2,0x20
+ ,0xD0,0x93,0xD0,0xBE,0xD1,0x80,0xD0,0xB8,0xD0,0xB7,0xD0,0xBE,0xD0,0xBD,0xD1
+ ,0x82,0x00,0xFE};
+/* end binary data. size = 93 bytes */
+
+
+/*
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ */
+
+/* begin binary data: way.o5m */
+uint8_t way_o5m_data[] = /* 175 */
+{0xFF,0xE0,0x04,0x6F,0x35,0x6D,0x32,0xFF,0x10,0x0E,0xE9,0xAB,0x21,0x00,0xC5
+ ,0x94,0x88,0x88,0x0A,0x8C,0xB1,0x9D,0xC3,0x04,0x10,0x08,0x04,0x00,0x9C,0x8F
+ ,0x0A,0x87,0xE7,0x05,0x10,0x07,0x04,0x00,0xF6,0x4A,0xFD,0x92,0x03,0x10,0x08
+ ,0x04,0x00,0xF6,0xA1,0x04,0xF5,0x80,0x04,0x10,0x08,0x04,0x00,0xB4,0xB6,0x02
+ ,0x89,0xBB,0x06,0x10,0x08,0x04,0x00,0xB4,0xEC,0x03,0xD9,0xCC,0x03,0x10,0x08
+ ,0x04,0x00,0xD6,0xC0,0x01,0xB7,0x83,0x03,0x10,0x08,0x06,0x00,0xDB,0xAB,0x02
+ ,0x9F,0xFE,0x02,0x10,0x08,0x02,0x00,0x87,0xAD,0x05,0xEB,0xA5,0x04,0xFF,0x11
+ ,0x44,0xCD,0xAB,0x21,0x00,0x0B,0xC9,0xAB,0x21,0x01,0x05,0x03,0x03,0x03,0x03
+ ,0x03,0x03,0x00,0x6E,0x61,0x6D,0x65,0x00,0x59,0x75,0x6B,0x6F,0x6E,0x20,0x52
+ ,0x69,0x76,0x65,0x72,0x00,0x00,0x6E,0x61,0x6D,0x65,0x3A,0x72,0x75,0x00,0xD0
+ ,0xAE,0xD0,0xBA,0xD0,0xBE,0xD0,0xBD,0x00,0x00,0x77,0x61,0x74,0x65,0x72,0x77
+ ,0x61,0x79,0x00,0x72,0x69,0x76,0x65,0x72,0x00,0xFE};
+/* end binary data. size = 175 bytes */
+
+
+/*
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ */
+
+/* begin binary data: relation.o5m*/
+uint8_t relation_o5m_data[] = /* 224 */
+{0xFF,0xE0,0x04,0x6F,0x35,0x6D,0x32,0xFF,0x10,0x2B,0xD7,0xAC,0x21,0x00,0xAD
+ ,0xCF,0xFC,0x87,0x0A,0xA6,0xCE,0x88,0xC3,0x04,0x00,0x6E,0x61,0x6D,0x65,0x00
+ ,0x57,0x68,0x69,0x74,0x65,0x68,0x6F,0x72,0x73,0x65,0x00,0x00,0x70,0x6C,0x61
+ ,0x63,0x65,0x00,0x74,0x6F,0x77,0x6E,0x00,0x10,0x07,0x2C,0x00,0x9A,0x90,0x24
+ ,0xD9,0x68,0x10,0x08,0x04,0x00,0xDE,0xCD,0x1E,0xF7,0xF6,0x15,0x10,0x08,0x04
+ ,0x00,0x91,0xD3,0x2A,0xFB,0xCC,0x11,0x10,0x08,0x04,0x00,0xE7,0x95,0x60,0xFE
+ ,0x9A,0x02,0x10,0x08,0x04,0x00,0x97,0xDE,0x13,0xC0,0xA7,0x0F,0x10,0x08,0x04
+ ,0x00,0xB6,0x9E,0x14,0xD2,0xC3,0x19,0x10,0x08,0x04,0x00,0xA6,0x8F,0x22,0xE0
+ ,0xDD,0x16,0x10,0x07,0x02,0x00,0xA6,0xF6,0x19,0x9F,0x49,0xFF,0x11,0x10,0x95
+ ,0xAC,0x21,0x00,0x0B,0x91,0xAC,0x21,0x01,0x03,0x03,0x03,0x03,0x03,0x03,0x1A
+ ,0xFF,0x12,0x46,0xB1,0xAC,0x21,0x00,0x11,0x95,0xAC,0x21,0x00,0x31,0x6F,0x75
+ ,0x74,0x65,0x72,0x00,0xD7,0xAC,0x21,0x00,0x30,0x00,0x00,0x6E,0x61,0x6D,0x65
+ ,0x00,0x57,0x68,0x69,0x74,0x65,0x68,0x6F,0x72,0x73,0x65,0x00,0x00,0x70,0x6C
+ ,0x61,0x63,0x65,0x00,0x74,0x6F,0x77,0x6E,0x00,0x00,0x74,0x79,0x70,0x65,0x00
+ ,0x6D,0x75,0x6C,0x74,0x69,0x70,0x6F,0x6C,0x79,0x67,0x6F,0x6E,0x00,0xFE};
+/* end binary data. size = 224 bytes */
+
+
+UNIT_TEST(OSM_O5M_Source_Node_read_test)
+{
+ std::string data(std::begin(node2_o5m_data), std::end(node2_o5m_data));
+ std::stringstream ss(data);
+
+ osm::O5MSourceReader dataset([&ss](uint8_t * buffer, size_t size)
+ {
+ return ss.read((char *)buffer, size).gcount();
+ });
+
+ osm::O5MSourceReader::iterator it = dataset.begin();
+ osm::O5MSourceReader::Entity const & em = *it;
+
+ CHECK_EQUAL(em.id, 513709898, ());
+ CHECK_EQUAL(em.user, string("Xmypblu"), ());
+ CHECK_EQUAL(em.uid, 395071, ());
+ CHECK_EQUAL(em.version, 8, ());
+ CHECK_EQUAL(em.changeset, 12059128, ());
+ CHECK_EQUAL(em.lon, 387666704, ());
+ CHECK_EQUAL(em.lat, 550927062, ());
+
+ auto const & tags = em.tags();
+ auto tag_iterator = tags.begin();
+ auto const & tag = *tag_iterator;
+ CHECK_EQUAL(tag.key, string("amenity"), ());
+ CHECK_EQUAL(tag.value, string("cinema"), ());
+ tag_iterator++;
+ CHECK_EQUAL(tag.key, string("name"), ());
+ CHECK_EQUAL(tag.value, string("КТ Горизонт"), ());
+ tag_iterator++;
+ CHECK(!(tag_iterator != tags.end()), ());
+}
+
+UNIT_TEST(OSM_O5M_Source_Way_read_test)
+{
+ std::string data(std::begin(way_o5m_data), std::end(way_o5m_data));
+ std::stringstream ss(data);
+
+ osm::O5MSourceReader dataset([&ss](uint8_t * buffer, size_t size)
+ {
+ return ss.read((char *)buffer, size).gcount();
+ });
+
+ set nodes;
+
+ vector> valid_tags = {
+ {"name", "Yukon River"},
+ {"name:ru", "Юкон"},
+ {"waterway", "river"}
+ };
+
+ for (auto const & em : dataset)
+ {
+ switch (em.type) {
+ case osm::O5MSourceReader::O5M_CMD_NODE:
+ {
+ nodes.insert(em.id);
+ for (auto const & tag : em.tags())
+ {
+ CHECK(false, ("Unexpected tag:", tag.key , tag.value));
+ }
+ break;
+ }
+ case osm::O5MSourceReader::O5M_CMD_WAY:
+ {
+ size_t nd_count = 0;
+ size_t tag_count = 0;
+ for (auto const &nd : em.nodes())
+ {
+ nd_count++;
+ CHECK(nodes.count(nd), ());
+ }
+ CHECK_EQUAL(nodes.size(), nd_count, ());
+ for (auto const & tag : em.tags())
+ {
+ CHECK_EQUAL(tag.key, valid_tags[tag_count].first, ());
+ CHECK_EQUAL(tag.value, valid_tags[tag_count].second, ());
+ tag_count++;
+ }
+ CHECK_EQUAL(valid_tags.size(), tag_count, ());
+ break;
+ }
+ default:
+ break;
+ }
+ }
+}
+
+UNIT_TEST(OSM_O5M_Source_Relation_read_test)
+{
+ std::string data(std::begin(relation_o5m_data), std::end(relation_o5m_data));
+ std::stringstream ss(data);
+
+ osm::O5MSourceReader dataset([&ss](uint8_t * buffer, size_t size)
+ {
+ return ss.read((char *)buffer, size).gcount();
+ });
+
+ set nodes;
+ set entities;
+
+ vector> node_valid_tags = {
+ {"name", "Whitehorse"},
+ {"place", "town"}
+ };
+
+ vector> relation_valid_tags = {
+ {"name", "Whitehorse"},
+ {"place", "town"},
+ {"type", "multipolygon"}
+ };
+
+ vector> relation_members = {
+ {osm::O5MSourceReader::O5M_CMD_WAY, "outer"},
+ {osm::O5MSourceReader::O5M_CMD_NODE, ""}
+ };
+
+ for (auto const & em : dataset)
+ {
+ entities.insert(em.id);
+
+ switch (em.type) {
+ case osm::O5MSourceReader::O5M_CMD_NODE:
+ {
+ nodes.insert(em.id);
+ size_t tag_count = 0;
+ for (auto const & tag : em.tags())
+ {
+ CHECK_EQUAL(tag.key, node_valid_tags[tag_count].first, ());
+ CHECK_EQUAL(tag.value, node_valid_tags[tag_count].second, ());
+ tag_count++;
+ }
+ break;
+ }
+ case osm::O5MSourceReader::O5M_CMD_WAY:
+ {
+ size_t nd_count = 0;
+ for (auto const &nd : em.nodes())
+ {
+ nd_count++;
+ CHECK(nodes.count(nd), ());
+ }
+ CHECK_EQUAL(nodes.size(), nd_count, ());
+ for (auto const & tag : em.tags())
+ {
+ CHECK(false, ("Unexpected tag:", tag.key , tag.value));
+ }
+ break;
+ }
+ case osm::O5MSourceReader::O5M_CMD_REL:
+ {
+ size_t member_count = 0;
+ size_t tag_count = 0;
+ for (auto const &member : em.members())
+ {
+ CHECK(entities.count(member.ref), ());
+ CHECK_EQUAL(relation_members[member_count].first, member.type, ("Current member:", member_count));
+ CHECK_EQUAL(relation_members[member_count].second, member.role, ("Current member:", member_count));
+ member_count++;
+ }
+ CHECK_EQUAL(member_count, 2, ());
+ for (auto const & tag : em.tags())
+ {
+ CHECK_EQUAL(tag.key, relation_valid_tags[tag_count].first, ());
+ CHECK_EQUAL(tag.value, relation_valid_tags[tag_count].second, ());
+ tag_count++;
+ }
+ CHECK_EQUAL(relation_valid_tags.size(), tag_count, ());
+ break;
+ }
+ default:
+ break;
+ }
+ }
+}
diff --git a/generator/osm_o5m_source.hpp b/generator/osm_o5m_source.hpp
new file mode 100644
index 0000000000..cd636440d3
--- /dev/null
+++ b/generator/osm_o5m_source.hpp
@@ -0,0 +1,567 @@
+//
+// osm_o5m_source.hpp
+// generator
+//
+// Created by Sergey Yershov on 14.04.15.
+//
+
+#pragma once
+
+#include "std/iostream.hpp"
+#include "std/sstream.hpp"
+#include "std/vector.hpp"
+#include "std/iomanip.hpp"
+
+namespace
+{
+ template
+ class StreamBuffer
+ {
+ using BufferT = std::vector;
+
+ ReaderT const & m_reader;
+ BufferT m_buffer;
+ size_t const m_buffer_base_size;
+ size_t m_byte_counter;
+
+ BufferT::value_type const * m_position;
+ BufferT::value_type const * m_start;
+ BufferT::value_type const * m_end;
+
+ public:
+ StreamBuffer(ReaderT const &reader, size_t buffer_size)
+ : m_reader(reader)
+ , m_buffer(buffer_size)
+ , m_buffer_base_size(buffer_size)
+ , m_byte_counter(0)
+ {
+ m_position = m_start = &m_buffer.front();
+ m_end = (&m_buffer.back())+1;
+ read_buffer();
+ }
+
+ size_t byte_counter()
+ {
+ size_t b = m_byte_counter;
+ m_byte_counter = 0;
+ return b;
+ }
+
+ inline BufferT::value_type get()
+ {
+ if(m_position == m_end)
+ read_buffer();
+ ++m_byte_counter;
+ return *(m_position++);
+ }
+
+ inline BufferT::value_type peek()
+ {
+ if(m_position == m_end)
+ read_buffer();
+ return *m_position;
+ }
+
+ void skip(size_t size = 1)
+ {
+ if(m_position + size >= m_end)
+ {
+ size -= (m_end - m_position);
+ for (read_buffer(); size > m_buffer.size(); size -= m_buffer.size())
+ read_buffer();
+ }
+ m_position += size;
+ m_byte_counter += size;
+ }
+
+ void read(BufferT::value_type * dest, size_t size) {
+ if(m_position + size >= m_end)
+ {
+ size_t remainder = (m_end - m_position);
+ memcpy(dest, m_position, remainder);
+ size -= remainder;
+ dest += remainder;
+ for (read_buffer(); size > m_buffer.size(); size -= m_buffer.size())
+ {
+ memcpy(dest, m_buffer.data(), m_buffer.size());
+ dest += m_buffer.size();
+ read_buffer();
+ }
+ }
+ memcpy(dest, m_position, size);
+ m_position += size;
+ }
+
+ private:
+
+ void read_buffer()
+ {
+ size_t readed_bytes = 0;
+
+ if (m_buffer.size() != m_buffer_base_size)
+ m_buffer.resize(m_buffer_base_size);
+
+ readed_bytes = m_reader(m_buffer.data(), m_buffer.size());
+
+ if (readed_bytes != m_buffer.size())
+ {
+ m_buffer.resize(readed_bytes);
+ m_start = &m_buffer.front();
+ m_end = (&m_buffer.back())+1;
+ }
+ m_position = m_start;
+ }
+ };
+} // anonymouse namespace
+
+namespace osm
+{
+
+ template
+ class O5MSource
+ {
+ template
+ using TReadFunc = std::function;
+
+ struct StringTableRec
+ {
+ char ksize;
+ char vsize;
+ unsigned short int align;
+ char k[254];
+ char v[254];
+ };
+
+ struct KeyValue
+ {
+ char const * key = nullptr;
+ char const * value = nullptr;
+
+ KeyValue() = default;
+ KeyValue(char const *k, char const *v) : key(k), value(v) {}
+ };
+
+ struct Member
+ {
+ int64_t ref = 0;
+ uint8_t type = 0;
+ char const * role = nullptr;
+ };
+
+ std::vector m_string_table;
+ std::vector m_string_buffer;
+ size_t m_string_current_index;
+ StreamBuffer m_buffer;
+ size_t m_remainder;
+ int64_t m_delta_state[3];
+ int64_t m_id = 0;
+ int32_t m_lon = 0;
+ int32_t m_lat = 0;
+ uint64_t m_timestamp = 0;
+ uint64_t m_changeset = 0;
+ int64_t m_refarea_size = 0;
+
+ public:
+ enum ECommand
+ {
+ O5M_CMD_END=0xfe,
+ O5M_CMD_NODE=0x10,
+ O5M_CMD_WAY=0x11,
+ O5M_CMD_REL=0x12,
+ O5M_CMD_BBOX=0xdb,
+ O5M_CMD_TSTAMP=0xdc,
+ O5M_CMD_HEADER=0xe0,
+ O5M_CMD_SYNC=0xee,
+ O5M_CMD_JUMP=0xef,
+ O5M_CMD_RESET=0xff
+ };
+
+ template
+ class SubElements
+ {
+ O5MSource * m_reader;
+ TFunc m_func;
+ public:
+
+ class iterator
+ {
+ O5MSource * m_reader;
+ TValue m_val;
+ TFunc m_func;
+ public:
+
+
+ iterator() : m_reader(nullptr) {}
+ explicit iterator(O5MSource *reader, TFunc const & func)
+ : m_reader(reader)
+ , m_func(func)
+ {
+ next_val();
+ }
+
+ bool operator != (iterator const &iter) {return m_reader != iter.m_reader;}
+
+ iterator & operator++() { next_val(); return *this; }
+ iterator operator++(int) { next_val(); return *this; }
+
+ void next_val() { m_reader = m_reader ? m_func(&m_val) : nullptr; }
+
+ TValue const & operator*() const { return m_val; }
+ };
+
+ SubElements(O5MSource * reader, TFunc const & func) : m_reader(reader), m_func(func) {}
+
+ void skip() { while(m_reader ? m_func(nullptr) : nullptr); }
+
+ iterator const begin() const { return iterator(m_reader, m_func); }
+ iterator const end() const { return iterator(); }
+ };
+
+ struct Entity
+ {
+ uint8_t type = 0;
+ int64_t id = 0;
+ uint64_t version = 0;
+ int32_t lon = 0;
+ int32_t lat = 0;
+ int64_t timestamp = 0;
+ int64_t changeset = 0;
+ uint64_t uid = 0;
+ char const * user = nullptr;
+
+ using RefsT = SubElements>;
+ using NodesT = SubElements>;
+ using TagsT = SubElements>;
+
+ RefsT members() const
+ {
+ return RefsT((type == O5M_CMD_REL) ? m_reader : nullptr, [this](Member *val)
+ {
+ return (m_reader) ? m_reader->ReadMember(val) : nullptr;
+ });
+ }
+
+ NodesT nodes() const
+ {
+ return NodesT((type == O5M_CMD_WAY) ? m_reader : nullptr, [this](int64_t *val)
+ {
+ return (m_reader) ? m_reader->ReadNd(val) : nullptr;
+ });
+ }
+
+ TagsT tags() const
+ {
+ members().skip();
+ nodes().skip();
+ bool const validType = (type == O5M_CMD_NODE || type == O5M_CMD_WAY || type == O5M_CMD_REL);
+ return TagsT( validType ? m_reader : nullptr, [this](KeyValue *val)
+ {
+ return (m_reader) ? m_reader->ReadStringPair(val) : nullptr;
+ });
+ }
+
+ void SkipRemainder()
+ {
+ if (!(type == O5M_CMD_NODE || type == O5M_CMD_WAY || type == O5M_CMD_REL))
+ return;
+ if (type == O5M_CMD_WAY)
+ while(m_reader ? m_reader->ReadNd(nullptr) : nullptr);
+ if (type == O5M_CMD_REL)
+ while(m_reader ? m_reader->ReadMember(nullptr) : nullptr);
+
+ while(m_reader ? m_reader->ReadStringPair(nullptr) : nullptr);
+ }
+
+ Entity() : m_reader(nullptr) {}
+ Entity(O5MSource * reader) : m_reader(reader) {}
+ protected:
+ O5MSource * m_reader;
+ };
+
+ uint64_t ReadUInt()
+ {
+ uint8_t b;
+ uint8_t i = 0;
+ uint64_t ret = 0LL;
+
+ do
+ {
+ b = m_buffer.get();
+ ret |= (uint64_t)(b & 0x7f) << (i++ * 7);
+ } while ( b & 0x80 );
+ size_t rb = m_buffer.byte_counter();
+ m_remainder -= rb;
+ m_refarea_size -= rb;
+ return ret;
+ }
+
+ int64_t ReadInt()
+ {
+ uint64_t ret = ReadUInt();
+ return (ret & 1) ? (-int64_t(ret >> 1) - 1) : int64_t(ret >> 1);
+ }
+
+ O5MSource * ReadMember(Member * ref)
+ {
+ if (m_refarea_size <= 0)
+ return nullptr;
+ int64_t delta = ReadInt();
+ KeyValue kv;
+ if(!ReadStringPair(&kv, true /*single string*/))
+ return nullptr;
+ m_delta_state[kv.key[0] - '0'] += delta;
+ if (ref)
+ {
+ ref->ref = m_delta_state[kv.key[0] - '0'];
+ ref->type = kv.key[0] - ' ';
+ ref->role = &kv.key[1];
+ }
+ return this;
+ }
+
+ O5MSource * ReadNd(int64_t * nd)
+ {
+ if (m_refarea_size <= 0)
+ return nullptr;
+ (nd ? *nd : m_delta_state[0]) = (m_delta_state[0] += ReadInt());
+ return this;
+ }
+
+ O5MSource * ReadStringPair(KeyValue * kv, bool single = false)
+ {
+ if (m_remainder == 0)
+ return nullptr;
+
+ uint64_t key = ReadUInt();
+ if (key)
+ {
+ // lookup table
+ if (kv)
+ *kv = KeyValue(m_string_table[m_string_current_index-key].k, m_string_table[m_string_current_index-key].v);
+ return this;
+ }
+
+ char *pBuf = m_string_buffer.data();
+ size_t sizes[2] = {0,0};
+ for (size_t i=0; i<(single?1:2); i++)
+ {
+ do
+ {
+ *pBuf = m_buffer.get();
+ sizes[i]++;
+ } while ( *(pBuf++) );
+ }
+ size_t rb = m_buffer.byte_counter();
+ m_remainder -= rb;
+ m_refarea_size -= rb;
+
+ if (sizes[0]+sizes[1] < 500)
+ {
+ memcpy(m_string_table[m_string_current_index].k, m_string_buffer.data(), sizes[0]);
+ memcpy(m_string_table[m_string_current_index].v, m_string_buffer.data()+sizes[0], sizes[1]);
+ size_t key = m_string_current_index++;
+ if (kv)
+ *kv = KeyValue(m_string_table[key].k, m_string_table[key].v);
+ return this;
+ }
+
+ if (kv)
+ *kv = KeyValue(m_string_buffer.data(), m_string_buffer.data() + sizes[0]);
+ return this;
+ }
+
+ void ReadIdAndVersion(Entity * e)
+ {
+ e->id = (m_id += ReadInt());
+
+ if ((e->version = ReadUInt()) == 0)
+ {
+ e->timestamp = 0;
+ e->changeset = 0;
+ e->uid = 0;
+ e->user = nullptr;
+ return;
+ }
+
+ e->timestamp = (m_timestamp += ReadInt());
+ if (m_timestamp)
+ {
+ e->changeset = (m_changeset += ReadInt());
+ KeyValue kv;
+ ReadStringPair(&kv);
+ uint8_t i = 0;
+ do e->uid |= (uint64_t)((*kv.key) & 0x7f) << (i++ * 7);
+ while ( *(kv.key++) & 0x80 );
+ e->user = kv.value;
+ }
+ }
+
+ void ReadLonLat(Entity * e)
+ {
+ e->lon = (m_lon += ReadInt());
+ e->lat = (m_lat += ReadInt());
+ }
+
+
+ O5MSource * ReadEntity(Entity * entity)
+ {
+
+ entity->SkipRemainder();
+
+ entity->type = m_buffer.get();
+
+ if (O5M_CMD_END == entity->type)
+ return nullptr;
+
+ if (O5M_CMD_RESET == entity->type)
+ {
+ Reset();
+ return ReadEntity(entity);
+ }
+
+ entity->uid = 0;
+ entity->user = nullptr;
+ m_refarea_size = 0;
+
+ m_remainder = ReadUInt(); /* entity size */
+
+ switch (entity->type) {
+ case O5M_CMD_NODE: {
+ ReadIdAndVersion(entity);
+ ReadLonLat(entity);
+ } break;
+ case O5M_CMD_WAY: {
+ ReadIdAndVersion(entity);
+ m_refarea_size = ReadUInt();
+ } break;
+ case O5M_CMD_REL: {
+ ReadIdAndVersion(entity);
+ m_refarea_size = ReadUInt();
+ } break;
+ case O5M_CMD_BBOX: {
+ } break;
+ case O5M_CMD_TSTAMP: {
+ ReadUInt();
+ } break;
+ case O5M_CMD_HEADER: {
+ } break;
+ case O5M_CMD_SYNC: {
+ } break;
+ case O5M_CMD_JUMP: {
+ } break;
+
+ default:
+ break;
+ }
+ return this;
+ }
+
+ void InitStringTable()
+ {
+ m_string_current_index = 0;
+ m_string_buffer.resize(1024);
+ m_string_table.resize(15000);
+ }
+
+ void Reset()
+ {
+ m_delta_state[0] = 0;
+ m_delta_state[1] = 0;
+ m_delta_state[2] = 0;
+ m_id = 0;
+ m_lon = 0;
+ m_lat = 0;
+ m_timestamp = 0;
+ m_changeset = 0;
+ m_remainder = 0;
+ }
+
+ public:
+
+ static char const * cmd_to_text(uint8_t cmd)
+ {
+ switch (cmd) {
+ case O5M_CMD_END: return "O5M_CMD_END"; break;
+ case O5M_CMD_NODE: return "O5M_CMD_NODE"; break;
+ case O5M_CMD_WAY: return "O5M_CMD_WAY"; break;
+ case O5M_CMD_REL: return "O5M_CMD_REL"; break;
+ case O5M_CMD_BBOX: return "O5M_CMD_BBOX"; break;
+ case O5M_CMD_TSTAMP: return "O5M_CMD_TSTAMP"; break;
+ case O5M_CMD_HEADER: return "O5M_CMD_HEADER"; break;
+ case O5M_CMD_SYNC: return "O5M_CMD_SYNC"; break;
+ case O5M_CMD_JUMP: return "O5M_CMD_JUMP"; break;
+ case O5M_CMD_RESET: return "O5M_CMD_RESET"; break;
+ default: return "Unknown command";
+ }
+ }
+
+ class iterator
+ {
+ O5MSource * m_reader;
+ Entity m_entity;
+
+ public:
+ iterator() : m_reader(nullptr), m_entity(nullptr) {}
+ iterator(O5MSource *reader) : m_reader(reader), m_entity(reader)
+ {
+ m_reader->Reset();
+ next_val();
+ }
+
+ bool operator != (iterator const &iter) {return m_reader != iter.m_reader;}
+ iterator & operator++() { next_val(); return *this; }
+ iterator operator++(int) { next_val(); return *this; }
+
+ void next_val() { m_reader = m_reader->ReadEntity(&m_entity) ? m_reader : nullptr; }
+
+ Entity const & operator*() const { return m_entity; }
+ };
+
+ iterator const begin() {return iterator(this);}
+ iterator const end() {return iterator();}
+
+ O5MSource(ReaderT const & reader) : m_buffer(reader, 10000 /* buffer size */)
+ {
+ if (m_buffer.get() != O5M_CMD_RESET)
+ {
+ throw std::runtime_error("Incorrect o5m start");
+ }
+ CheckHeader();
+ InitStringTable();
+ }
+
+ bool CheckHeader()
+ {
+ size_t len = 4;
+ if (m_buffer.get() == O5M_CMD_HEADER && ReadUInt() == len)
+ {
+ std::string sign(len,' ');
+ m_buffer.read((uint8_t *)sign.data(), len);
+ if (sign == "o5m2" || sign == "o5c2")
+ return true;
+ }
+ return false;
+ }
+
+ friend std::ostream & operator << (std::ostream & s, O5MSource::Entity const & em)
+ {
+ s << cmd_to_text(em.type) << " ID: " << em.id;
+ if (em.version)
+ {
+ std::time_t timestamp = em.timestamp;
+ std::tm stm = *std::gmtime(×tamp);
+ s << " Version: " << em.version << " timestamp: " << std::put_time(&stm, "%FT%TZ");
+ s << " changeset: " << em.changeset << " uid: " << em.uid << " user: " << em.user;
+ }
+ if (em.type == O5M_CMD_NODE)
+ {
+ s << std::endl << " lon: " << em.lon << " lat: " << em.lat;
+ }
+ return s;
+ }
+ };
+
+ using TReadFunc = std::function;
+ typedef O5MSource O5MSourceReader;
+
+} // namespace osm
diff --git a/generator/osm_source.cpp b/generator/osm_source.cpp
index 2df5d11608..a10d7ebfd1 100644
--- a/generator/osm_source.cpp
+++ b/generator/osm_source.cpp
@@ -22,48 +22,11 @@
#include "coding/parse_xml.hpp"
#include "3party/o5mreader/o5mreader.h"
+#include "generator/source_reader.hpp"
#define DECODE_O5M_COORD(coord) (static_cast(coord) / 1E+7)
-namespace
-{
- class SourceReader
- {
- string const &m_filename;
- FILE * m_file;
-
- public:
- SourceReader(string const & filename) : m_filename(filename)
- {
- if (m_filename.empty())
- {
- LOG(LINFO, ("Read OSM data from stdin..."));
- m_file = freopen(NULL, "rb", stdin);
- }
- else
- {
- LOG(LINFO, ("Read OSM data from", filename));
- m_file = fopen(filename.c_str(), "rb");
- }
- }
-
- ~SourceReader()
- {
- if (!m_filename.empty())
- fclose(m_file);
- }
-
- inline FILE * Handle() { return m_file; }
-
- uint64_t Read(char * buffer, uint64_t bufferSize)
- {
- return fread(buffer, sizeof(char), bufferSize, m_file);
- }
- };
-}
-
-
namespace feature
{
diff --git a/generator/source_reader.hpp b/generator/source_reader.hpp
new file mode 100644
index 0000000000..fb208b3a8b
--- /dev/null
+++ b/generator/source_reader.hpp
@@ -0,0 +1,42 @@
+#pragma once
+
+#include "std/string.hpp"
+#include "std/cstdio.hpp"
+
+namespace
+{
+ class SourceReader
+ {
+ string const &m_filename;
+ FILE * m_file;
+
+ public:
+ SourceReader(string const & filename) : m_filename(filename)
+ {
+ if (m_filename.empty())
+ {
+ LOG(LINFO, ("Read OSM data from stdin..."));
+ m_file = freopen(NULL, "rb", stdin);
+ }
+ else
+ {
+ LOG(LINFO, ("Read OSM data from", filename));
+ m_file = fopen(filename.c_str(), "rb");
+ }
+ }
+
+ ~SourceReader()
+ {
+ if (!m_filename.empty())
+ fclose(m_file);
+ }
+
+ inline FILE * Handle() { return m_file; }
+
+ uint64_t Read(char * buffer, uint64_t bufferSize)
+ {
+ return fread(buffer, sizeof(char), bufferSize, m_file);
+ }
+ };
+}
+