From 32af6db6fd63955565a2b26533eb9015ec2f1b56 Mon Sep 17 00:00:00 2001 From: Yuri Gorshenin Date: Mon, 18 Jul 2016 16:39:49 +0300 Subject: [PATCH] [indexer] Implemented CentersTable. --- coding/coding.pro | 1 + coding/memory_region.hpp | 48 +++ coding/reader.hpp | 46 +++ coding/succinct_mapper.hpp | 14 +- indexer/centers_table.cpp | 337 +++++++++++++++++++ indexer/centers_table.hpp | 79 +++++ indexer/indexer.pro | 2 + indexer/indexer_tests/centers_table_test.cpp | 113 +++++++ indexer/indexer_tests/indexer_tests.pro | 1 + indexer/rank_table.cpp | 42 +-- 10 files changed, 635 insertions(+), 48 deletions(-) create mode 100644 coding/memory_region.hpp create mode 100644 indexer/centers_table.cpp create mode 100644 indexer/centers_table.hpp create mode 100644 indexer/indexer_tests/centers_table_test.cpp diff --git a/coding/coding.pro b/coding/coding.pro index 638202e5be..c2af2eb04f 100644 --- a/coding/coding.pro +++ b/coding/coding.pro @@ -65,6 +65,7 @@ HEADERS += \ internal/file_data.hpp \ internal/xmlparser.hpp \ matrix_traversal.hpp \ + memory_region.hpp \ mmap_reader.hpp \ multilang_utf8_string.hpp \ parse_xml.hpp \ diff --git a/coding/memory_region.hpp b/coding/memory_region.hpp new file mode 100644 index 0000000000..b7a1994d95 --- /dev/null +++ b/coding/memory_region.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include "coding/file_container.hpp" + +#include "base/macros.hpp" + +#include "std/cstdint.hpp" + +class MemoryRegion +{ +public: + virtual ~MemoryRegion() = default; + + virtual uint64_t Size() const = 0; + virtual uint8_t const * ImmutableData() const = 0; +}; + +class MappedMemoryRegion : public MemoryRegion +{ +public: + MappedMemoryRegion(FilesMappingContainer::Handle && handle) : m_handle(move(handle)) {} + + // MemoryRegion overrides: + uint64_t Size() const override { return m_handle.GetSize(); } + uint8_t const * ImmutableData() const override { return m_handle.GetData(); } + +private: + FilesMappingContainer::Handle m_handle; + + DISALLOW_COPY(MappedMemoryRegion); +}; + +class CopiedMemoryRegion : public MemoryRegion +{ +public: + CopiedMemoryRegion(vector && buffer) : m_buffer(move(buffer)) {} + + // MemoryRegion overrides: + uint64_t Size() const override { return m_buffer.size(); } + uint8_t const * ImmutableData() const override { return m_buffer.data(); } + + inline uint8_t * MutableData() { return m_buffer.data(); } + +private: + vector m_buffer; + + DISALLOW_COPY(CopiedMemoryRegion); +}; diff --git a/coding/reader.hpp b/coding/reader.hpp index df70e03143..6c8ed98cb3 100644 --- a/coding/reader.hpp +++ b/coding/reader.hpp @@ -132,6 +132,52 @@ public: inline string const & GetName() const { return m_p->GetName(); } }; +// Source that reads from a reader. +class NonOwningReaderSource +{ +public: + NonOwningReaderSource(Reader const & reader) : m_reader(reader), m_pos(0) {} + + void Read(void * p, size_t size) + { + m_reader.Read(m_pos, p, size); + m_pos += size; + CheckPosition(); + } + + void Skip(uint64_t size) + { + m_pos += size; + CheckPosition(); + } + + uint64_t Pos() const { return m_pos; } + + uint64_t Size() const + { + CheckPosition(); + return (m_reader.Size() - m_pos); + } + + // unique_ptr SubReader(uint64_t size) + // { + // uint64_t const pos = m_pos; + // Skip(size); + // return m_reader.SubReader(pos, size); + // } + + // unique_ptr SubReader() { return SubReader(Size()); } + +private: + void CheckPosition() const + { + ASSERT_LESS_OR_EQUAL(m_pos, m_reader.Size(), (m_pos, m_reader.Size())); + } + + Reader const & m_reader; + uint64_t m_pos; +}; + // Source that reads from a reader. template class ReaderSource diff --git a/coding/succinct_mapper.hpp b/coding/succinct_mapper.hpp index 30d413f732..e6f856bbc6 100644 --- a/coding/succinct_mapper.hpp +++ b/coding/succinct_mapper.hpp @@ -21,7 +21,7 @@ static T * Align8Ptr(T * ptr) inline uint32_t ToAlign8(uint64_t written) { return (0x8 - (written & 0x7)) & 0x7; } -inline bool IsAligned(uint64_t offset) { return ToAlign8(offset) == 0; } +inline bool IsAlign8(uint64_t offset) { return ToAlign8(offset) == 0; } template void WritePadding(TWriter & writer, uint64_t & bytesWritten) @@ -139,7 +139,7 @@ public: typename enable_if::value, FreezeVisitor &>::type operator()(T & val, char const * /* name */) { - ASSERT(IsAligned(m_writer.Pos()), ()); + ASSERT(IsAlign8(m_writer.Pos()), ()); val.map(*this); return *this; } @@ -148,7 +148,7 @@ public: typename enable_if::value, FreezeVisitor &>::type operator()(T & val, char const * /* name */) { - ASSERT(IsAligned(m_writer.Pos()), ()); + ASSERT(IsAlign8(m_writer.Pos()), ()); m_writer.Write(&val, sizeof(T)); m_bytesWritten += sizeof(T); WritePadding(m_writer, m_bytesWritten); @@ -158,7 +158,7 @@ public: template FreezeVisitor & operator()(succinct::mapper::mappable_vector & vec, char const * /* name */) { - ASSERT(IsAligned(m_writer.Pos()), ()); + ASSERT(IsAlign8(m_writer.Pos()), ()); (*this)(vec.m_size, "size"); size_t const bytes = static_cast(vec.m_size * sizeof(T)); @@ -187,7 +187,7 @@ public: typename enable_if::value, ReverseFreezeVisitor &>::type operator()( T & val, char const * /* name */) { - ASSERT(IsAligned(m_writer.Pos()), ()); + ASSERT(IsAlign8(m_writer.Pos()), ()); val.map(*this); return *this; } @@ -196,7 +196,7 @@ public: typename enable_if::value, ReverseFreezeVisitor &>::type operator()( T & val, char const * /* name */) { - ASSERT(IsAligned(m_writer.Pos()), ()); + ASSERT(IsAlign8(m_writer.Pos()), ()); T const reversedVal = ReverseByteOrder(val); m_writer.Write(&reversedVal, sizeof(reversedVal)); m_bytesWritten += sizeof(T); @@ -208,7 +208,7 @@ public: ReverseFreezeVisitor & operator()(succinct::mapper::mappable_vector & vec, char const * /* name */) { - ASSERT(IsAligned(m_writer.Pos()), ()); + ASSERT(IsAlign8(m_writer.Pos()), ()); (*this)(vec.m_size, "size"); for (auto const & val : vec) diff --git a/indexer/centers_table.cpp b/indexer/centers_table.cpp new file mode 100644 index 0000000000..534eec573d --- /dev/null +++ b/indexer/centers_table.cpp @@ -0,0 +1,337 @@ +#include "indexer/centers_table.hpp" + +#include "indexer/coding_params.hpp" +#include "indexer/geometry_coding.hpp" +#include "indexer/point_to_int64.hpp" + +#include "coding/endianness.hpp" +#include "coding/memory_region.hpp" +#include "coding/reader.hpp" +#include "coding/succinct_mapper.hpp" +#include "coding/varint.hpp" +#include "coding/write_to_sink.hpp" +#include "coding/writer.hpp" + +#include "base/assert.hpp" +#include "base/logging.hpp" + +#include "std/unordered_map.hpp" + +#include "3party/succinct/elias_fano.hpp" +#include "3party/succinct/rs_bit_vector.hpp" + +namespace search +{ +namespace +{ +template +void EndiannesAwareMap(bool endiannesMismatch, CopiedMemoryRegion & region, TCont & cont) +{ + TCont c; + if (endiannesMismatch) + { + coding::ReverseMapVisitor visitor(region.MutableData()); + c.map(visitor); + } + else + { + coding::MapVisitor visitor(region.ImmutableData()); + c.map(visitor); + } + + c.swap(cont); +} + +// V0 of CentersTable. Has the following format: +// +// File offset (bytes) Field name Field size (bytes) +// 4 positions offset 4 +// 8 deltas offset 4 +// 12 end of section 4 +// 16 identifiers table positions offset - 16 +// positions offset positions table deltas offset - positions offset +// deltas offset deltas blocks end of section - deltas offset +// +// All offsets are in little-endian format. +// +// Identifiers table is a bit-vector with rank-select table, where set +// bits denote that centers for the corresponding features are in +// table. Identifiers table is stored in a native endianness. +// +// Positions table is a elias-fano table, where each entry corresponds +// to the start position of the centers block. Positions table is +// stored in a native endianness. +// +// Deltas is a sequence of blocks, where each block is a sequence of +// 64 varuints, where each varuint represents an encoded delta of the +// center from some prediction. For the first center in the block map +// base point is used as a prediction, for all other centers in the +// block previous center is used as a prediction. This allows to +// decode block of 64 consecutive centers at one syscall, and cache +// them in RAM. +class CentersTableV0 : public CentersTable +{ +public: + static const uint32_t kFrequency = 64; + + struct Header + { + void Read(Reader & reader) + { + m_base.Read(reader); + + NonOwningReaderSource source(reader); + source.Skip(sizeof(m_base)); + m_positionsOffset = ReadPrimitiveFromSource(source); + m_deltasOffset = ReadPrimitiveFromSource(source); + m_endOffset = ReadPrimitiveFromSource(source); + } + + void Write(Writer & writer) + { + m_base.Write(writer); + + WriteToSink(writer, m_positionsOffset); + WriteToSink(writer, m_deltasOffset); + WriteToSink(writer, m_endOffset); + } + + bool IsValid() const + { + if (!m_base.IsValid()) + { + LOG(LERROR, ("Base header is not valid!")); + return false; + } + if (m_positionsOffset < sizeof(m_header)) + { + LOG(LERROR, ("Positions before header:", m_positionsOffset, sizeof(m_header))); + return false; + } + if (m_deltasOffset < m_positionsOffset) + { + LOG(LERROR, ("Deltas before positions:", m_deltasOffset, m_positionsOffset)); + return false; + } + if (m_endOffset < m_deltasOffset) + { + LOG(LERROR, ("End of section before deltas:", m_endOffset, m_deltasOffset)); + return false; + } + return true; + } + + CentersTable::Header m_base; + uint32_t m_positionsOffset = 0; + uint32_t m_deltasOffset = 0; + uint32_t m_endOffset = 0; + }; + + static_assert(sizeof(Header) == 16, "Wrong header size."); + + CentersTableV0(Reader & reader, serial::CodingParams const & codingParams) + : m_reader(reader), m_codingParams(codingParams) + { + } + + // CentersTable overrides: + bool Get(uint32_t id, m2::PointD & center) override + { + if (id >= m_ids.size() || !m_ids[id]) + return false; + uint32_t const rank = static_cast(m_ids.rank(id)); + uint32_t const base = rank / kFrequency; + uint32_t const offset = rank % kFrequency; + + auto & entry = m_cache[base]; + if (entry.empty()) + { + entry.resize(kFrequency); + + uint32_t const start = m_offsets.select(base); + uint32_t const end = base + 1 < m_offsets.num_ones() + ? m_offsets.select(base + 1) + : m_header.m_endOffset - m_header.m_deltasOffset; + + vector data(end - start); + + m_reader.Read(m_header.m_deltasOffset + start, data.data(), data.size()); + + MemReader mreader(data.data(), data.size()); + NonOwningReaderSource msource(mreader); + + uint64_t delta = ReadVarUint(msource); + entry[0] = DecodeDelta(delta, m_codingParams.GetBasePoint()); + + for (size_t i = 1; i < kFrequency && msource.Size() > 0; ++i) + { + delta = ReadVarUint(msource); + entry[i] = DecodeDelta(delta, entry[i - 1]); + } + } + + center = PointU2PointD(entry[offset], m_codingParams.GetCoordBits()); + return true; + } + +private: + // CentersTable overrides: + bool Init() override + { + m_header.Read(m_reader); + + if (!m_header.IsValid()) + return false; + + bool const isHostBigEndian = IsBigEndian(); + bool const isDataBigEndian = m_header.m_base.m_endiannes == 1; + bool const endiannesMismatch = isHostBigEndian != isDataBigEndian; + + { + uint32_t const idsSize = m_header.m_positionsOffset - sizeof(m_header); + vector data(idsSize); + m_reader.Read(sizeof(m_header), data.data(), data.size()); + m_idsRegion = make_unique(move(data)); + EndiannesAwareMap(endiannesMismatch, *m_idsRegion, m_ids); + } + + { + uint32_t const offsetsSize = m_header.m_deltasOffset - m_header.m_positionsOffset; + vector data(offsetsSize); + m_reader.Read(m_header.m_positionsOffset, data.data(), data.size()); + m_offsetsRegion = make_unique(move(data)); + EndiannesAwareMap(endiannesMismatch, *m_offsetsRegion, m_offsets); + } + + return true; + } + +private: + Header m_header; + Reader & m_reader; + serial::CodingParams const m_codingParams; + + unique_ptr m_idsRegion; + unique_ptr m_offsetsRegion; + + succinct::rs_bit_vector m_ids; + succinct::elias_fano m_offsets; + + unordered_map> m_cache; +}; +} // namespace + +// CentersTable::Header ---------------------------------------------------------------------------- +void CentersTable::Header::Read(Reader & reader) +{ + NonOwningReaderSource source(reader); + m_version = ReadPrimitiveFromSource(source); + m_endiannes = ReadPrimitiveFromSource(source); +} + +void CentersTable::Header::Write(Writer & writer) +{ + WriteToSink(writer, m_version); + WriteToSink(writer, m_endiannes); +} + +bool CentersTable::Header::IsValid() const +{ + if (m_endiannes > 1) + return false; + return true; +} + +// CentersTable ------------------------------------------------------------------------------------ +unique_ptr CentersTable::Load(Reader & reader, + serial::CodingParams const & codingParams) +{ + uint16_t const version = ReadPrimitiveFromPos(reader, 0 /* pos */); + if (version != 0) + return unique_ptr(); + + // Only single version of centers table is supported now. If you + // need to implement new versions of CentersTable, implement + // dispatching based on first-four-bytes version. + unique_ptr table = make_unique(reader, codingParams); + if (!table->Init()) + return unique_ptr(); + return table; +} + +// CentersTableBuilder ----------------------------------------------------------------------------- +CentersTableBuilder::CentersTableBuilder(Writer & writer, serial::CodingParams const & codingParams) + : m_writer(writer), m_codingParams(codingParams) +{ +} + +CentersTableBuilder::~CentersTableBuilder() +{ + CentersTableV0::Header header; + + int64_t const startOffset = m_writer.Pos(); + header.Write(m_writer); + + { + uint64_t const numBits = m_ids.empty() ? 0 : m_ids.back() + 1; + + succinct::bit_vector_builder builder(numBits); + for (auto const & id : m_ids) + builder.set(id, true); + + coding::FreezeVisitor visitor(m_writer); + succinct::rs_bit_vector(&builder).map(visitor); + } + + vector offsets; + vector deltas; + + { + MemWriter> writer(deltas); + for (size_t i = 0; i < m_centers.size(); i += CentersTableV0::kFrequency) + { + offsets.push_back(deltas.size()); + + uint64_t delta = EncodeDelta(m_centers[i], m_codingParams.GetBasePoint()); + WriteVarUint(writer, delta); + for (size_t j = i + 1; j < i + CentersTableV0::kFrequency && j < m_centers.size(); ++j) + { + delta = EncodeDelta(m_centers[j], m_centers[j - 1]); + WriteVarUint(writer, delta); + } + } + } + + { + succinct::elias_fano::elias_fano_builder builder(offsets.empty() ? 0 : offsets.back() + 1, + offsets.size()); + for (auto const & offset : offsets) + builder.push_back(offset); + + header.m_positionsOffset = m_writer.Pos(); + coding::FreezeVisitor visitor(m_writer); + succinct::elias_fano(&builder).map(visitor); + } + + { + header.m_deltasOffset = m_writer.Pos(); + m_writer.Write(deltas.data(), deltas.size()); + header.m_endOffset = m_writer.Pos(); + } + + int64_t const endOffset = m_writer.Pos(); + + m_writer.Seek(startOffset); + m_writer.Write(&header, sizeof(header)); + m_writer.Seek(endOffset); +} + +void CentersTableBuilder::Put(uint32_t featureId, m2::PointD const & center) +{ + if (!m_ids.empty()) + CHECK_LESS(m_ids.back(), featureId, ()); + + m_centers.push_back(PointD2PointU(center, m_codingParams.GetCoordBits())); + m_ids.push_back(featureId); +} +} // namespace search diff --git a/indexer/centers_table.hpp b/indexer/centers_table.hpp new file mode 100644 index 0000000000..7c67eed682 --- /dev/null +++ b/indexer/centers_table.hpp @@ -0,0 +1,79 @@ +#pragma once + +#include "geometry/point2d.hpp" + +#include "std/cstdint.hpp" +#include "std/unique_ptr.hpp" +#include "std/vector.hpp" + +class Reader; +class Writer; + +namespace serial +{ +class CodingParams; +} + +namespace search +{ +// A wrapper class around serialized centers-table. +// +// *NOTE* This wrapper is abstract enough so feel free to change it, +// but note that there should always be backward-compatibility. Thus, +// when adding new versions, never change data format of old versions. +// +// All centers tables are serialized in the following format: +// +// File offset (bytes) Field name Field size (bytes) +// 0 version 2 +// 2 endiannes 2 +// +// Version and endiannes is always in stored little-endian format. 0 +// value of endiannes means little endian, whereas 1 means big endian. +class CentersTable +{ +public: + struct Header + { + void Read(Reader & reader); + void Write(Writer & writer); + bool IsValid() const; + + uint16_t m_version = 0; + uint16_t m_endiannes = 0; + }; + + static_assert(sizeof(Header) == 4, "Wrong header size"); + + virtual ~CentersTable() = default; + + // Tries to get |center| of the feature identified by |id|. Returns + // false if table does not have entry for the feature. + virtual bool Get(uint32_t id, m2::PointD & center) = 0; + + // Loads CentersTable instance. Note that |reader| must be alive + // until the destruction of loaded table. Returns nullptr if + // CentersTable can't be loaded. + static unique_ptr Load(Reader & reader, serial::CodingParams const & codingParams); + +private: + virtual bool Init() = 0; +}; + +class CentersTableBuilder +{ +public: + CentersTableBuilder(Writer & writer, serial::CodingParams const & codingParams); + + ~CentersTableBuilder(); + + void Put(uint32_t featureId, m2::PointD const & center); + +private: + Writer & m_writer; + serial::CodingParams const & m_codingParams; + + vector m_centers; + vector m_ids; +}; +} // namespace search diff --git a/indexer/indexer.pro b/indexer/indexer.pro index ee3e3624ef..26a2499e36 100644 --- a/indexer/indexer.pro +++ b/indexer/indexer.pro @@ -13,6 +13,7 @@ SOURCES += \ categories_holder.cpp \ categories_holder_loader.cpp \ categories_index.cpp \ + centers_table.cpp \ classificator.cpp \ classificator_loader.cpp \ coding_params.cpp \ @@ -64,6 +65,7 @@ HEADERS += \ categories_index.hpp \ cell_coverer.hpp \ cell_id.hpp \ + centers_table.hpp \ classificator.hpp \ classificator_loader.hpp \ coding_params.hpp \ diff --git a/indexer/indexer_tests/centers_table_test.cpp b/indexer/indexer_tests/centers_table_test.cpp new file mode 100644 index 0000000000..107b3d1e56 --- /dev/null +++ b/indexer/indexer_tests/centers_table_test.cpp @@ -0,0 +1,113 @@ +#include "testing/testing.hpp" + +#include "indexer/centers_table.hpp" +#include "indexer/classificator_loader.hpp" +#include "indexer/data_header.hpp" +#include "indexer/feature_algo.hpp" +#include "indexer/features_vector.hpp" + +#include "platform/platform.hpp" + +#include "coding/file_name_utils.hpp" +#include "coding/reader.hpp" +#include "coding/writer.hpp" + +#include "geometry/mercator.hpp" +#include "geometry/point2d.hpp" + +#include "std/cstdint.hpp" +#include "std/string.hpp" +#include "std/utility.hpp" +#include "std/vector.hpp" + +using namespace search; + +namespace +{ +using TBuffer = vector; + +struct CentersTableTest +{ + CentersTableTest() { classificator::Load(); } +}; + +UNIT_CLASS_TEST(CentersTableTest, Smoke) +{ + string const kMap = my::JoinFoldersToPath(GetPlatform().WritableDir(), "minsk-pass.mwm"); + + feature::DataHeader header(kMap); + auto const codingParams = header.GetDefCodingParams(); + + FeaturesVectorTest fv(kMap); + + TBuffer buffer; + + { + MemWriter writer(buffer); + CentersTableBuilder builder(writer, codingParams); + + fv.GetVector().ForEach( + [&](FeatureType & ft, uint32_t id) { builder.Put(id, feature::GetCenter(ft)); }); + } + + { + MemReader reader(buffer.data(), buffer.size()); + auto table = CentersTable::Load(reader, codingParams); + TEST(table.get(), ()); + + fv.GetVector().ForEach([&](FeatureType & ft, uint32_t id) { + m2::PointD actual; + TEST(table->Get(id, actual), ()); + + m2::PointD expected = feature::GetCenter(ft); + + TEST_LESS_OR_EQUAL(MercatorBounds::DistanceOnEarth(actual, expected), 1, (id)); + }); + } +} + +UNIT_CLASS_TEST(CentersTableTest, Subset) +{ + vector> const features = { + {1, m2::PointD(0, 0)}, {5, m2::PointD(1, 1)}, {10, m2::PointD(2, 2)}}; + + serial::CodingParams codingParams; + + TBuffer buffer; + { + MemWriter writer(buffer); + CentersTableBuilder builder(writer, codingParams); + for (auto const & feature : features) + builder.Put(feature.first, feature.second); + } + + { + MemReader reader(buffer.data(), buffer.size()); + auto table = CentersTable::Load(reader, codingParams); + TEST(table.get(), ()); + + size_t i = 0; + size_t j = 0; + + while (i < 100) + { + ASSERT(j == features.size() || features[j].first >= i, ("Invariant violation")); + + m2::PointD actual; + if (j != features.size() && i == features[j].first) + { + TEST(table->Get(i, actual), ()); + TEST_LESS_OR_EQUAL(MercatorBounds::DistanceOnEarth(actual, features[j].second), 1, ()); + } + else + { + TEST(!table->Get(i, actual), ()); + } + + ++i; + while (j != features.size() && features[j].first < i) + ++j; + } + } +} +} // namespace diff --git a/indexer/indexer_tests/indexer_tests.pro b/indexer/indexer_tests/indexer_tests.pro index 0f1090a2f0..4eaa571965 100644 --- a/indexer/indexer_tests/indexer_tests.pro +++ b/indexer/indexer_tests/indexer_tests.pro @@ -20,6 +20,7 @@ SOURCES += \ categories_test.cpp \ cell_coverer_test.cpp \ cell_id_test.cpp \ + centers_table_test.cpp \ checker_test.cpp \ drules_selector_parser_test.cpp \ editable_map_object_test.cpp \ diff --git a/indexer/rank_table.cpp b/indexer/rank_table.cpp index 3d9d6300fd..80c3e687cc 100644 --- a/indexer/rank_table.cpp +++ b/indexer/rank_table.cpp @@ -13,6 +13,7 @@ #include "coding/endianness.hpp" #include "coding/file_container.hpp" #include "coding/file_writer.hpp" +#include "coding/memory_region.hpp" #include "coding/reader.hpp" #include "coding/simple_dense_coding.hpp" #include "coding/succinct_mapper.hpp" @@ -55,47 +56,6 @@ CheckResult CheckEndianness(TReader && reader) return CheckResult::EndiannessMatch; } -class MemoryRegion -{ -public: - virtual ~MemoryRegion() = default; - - virtual uint64_t Size() const = 0; - virtual uint8_t const * ImmutableData() const = 0; -}; - -class MappedMemoryRegion : public MemoryRegion -{ -public: - MappedMemoryRegion(FilesMappingContainer::Handle && handle) : m_handle(move(handle)) {} - - // MemoryRegion overrides: - uint64_t Size() const override { return m_handle.GetSize(); } - uint8_t const * ImmutableData() const override { return m_handle.GetData(); } - -private: - FilesMappingContainer::Handle m_handle; - - DISALLOW_COPY(MappedMemoryRegion); -}; - -class CopiedMemoryRegion : public MemoryRegion -{ -public: - CopiedMemoryRegion(vector && buffer) : m_buffer(move(buffer)) {} - - // MemoryRegion overrides: - uint64_t Size() const override { return m_buffer.size(); } - uint8_t const * ImmutableData() const override { return m_buffer.data(); } - - inline uint8_t * MutableData() { return m_buffer.data(); } - -private: - vector m_buffer; - - DISALLOW_COPY(CopiedMemoryRegion); -}; - unique_ptr GetMemoryRegionForTag(FilesContainerR const & rcont, FilesContainerBase::Tag const & tag) {