Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-22 13:58:55 +01:00
parent 4af19165ec
commit 68073add76
12458 changed files with 12350765 additions and 2 deletions

View file

@ -0,0 +1,68 @@
#include "storage/diff_scheme/apply_diff.hpp"
#include "platform/platform.hpp"
#include "coding/internal/file_data.hpp"
#include "base/assert.hpp"
#include "base/cancellable.hpp"
namespace storage
{
namespace diffs
{
void ApplyDiff(ApplyDiffParams && p, base::Cancellable const & cancellable, OnDiffApplicationFinished const & task)
{
using namespace generator::mwm_diff;
GetPlatform().RunTask(Platform::Thread::File, [p = std::move(p), &cancellable, task]
{
CHECK(p.m_diffFile, ());
CHECK(p.m_oldMwmFile, ());
auto & diffReadyPath = p.m_diffReadyPath;
auto & diffFile = p.m_diffFile;
auto const diffPath = diffFile->GetPath(MapFileType::Diff);
auto result = DiffApplicationResult::Failed;
diffFile->SyncWithDisk();
if (!diffFile->OnDisk(MapFileType::Diff))
{
base::RenameFileX(diffReadyPath, diffPath);
diffFile->SyncWithDisk();
}
auto const isFilePrepared = diffFile->OnDisk(MapFileType::Diff);
if (isFilePrepared)
{
std::string const oldMwmPath = p.m_oldMwmFile->GetPath(MapFileType::Map);
std::string const newMwmPath = diffFile->GetPath(MapFileType::Map);
std::string const diffApplyingInProgressPath = newMwmPath + DIFF_APPLYING_FILE_EXTENSION;
result = generator::mwm_diff::ApplyDiff(oldMwmPath, diffApplyingInProgressPath, diffPath, cancellable);
if (result == DiffApplicationResult::Ok && !base::RenameFileX(diffApplyingInProgressPath, newMwmPath))
result = DiffApplicationResult::Failed;
Platform::RemoveFileIfExists(diffApplyingInProgressPath);
if (result != DiffApplicationResult::Ok)
Platform::RemoveFileIfExists(newMwmPath);
}
switch (result)
{
case DiffApplicationResult::Ok: diffFile->DeleteFromDisk(MapFileType::Diff); break;
case DiffApplicationResult::Cancelled:
// The diff file will be deleted by storage.
// Another way would be to leave it on disk but all consequences
// of interacting with storage are much harder to be taken into account that way.
break;
case DiffApplicationResult::Failed: diffFile->DeleteFromDisk(MapFileType::Diff); break;
}
GetPlatform().RunTask(Platform::Thread::Gui, [task, result]() { task(result); });
});
}
} // namespace diffs
} // namespace storage

View file

@ -0,0 +1,29 @@
#pragma once
#include "mwm_diff/diff.hpp"
#include "storage/storage_defines.hpp"
#include <functional>
namespace base
{
class Cancellable;
}
namespace storage
{
namespace diffs
{
struct ApplyDiffParams
{
std::string m_diffReadyPath;
LocalFilePtr m_diffFile;
LocalFilePtr m_oldMwmFile;
};
using OnDiffApplicationFinished = std::function<void(generator::mwm_diff::DiffApplicationResult)>;
void ApplyDiff(ApplyDiffParams && p, base::Cancellable const & cancellable, OnDiffApplicationFinished const & task);
} // namespace diffs
} // namespace storage

View file

@ -0,0 +1,148 @@
#include "storage/diff_scheme/diff_scheme_loader.hpp"
#include "platform/http_client.hpp"
#include "platform/platform.hpp"
#include "base/logging.hpp"
#include <memory>
#include <sstream>
#include <string>
#include <unordered_map>
#include <utility>
#include "cppjansson/cppjansson.hpp"
#include "private.h"
using namespace std;
namespace
{
using namespace storage::diffs;
char constexpr kMaxVersionKey[] = "max_version";
char constexpr kMwmsKey[] = "mwms";
char constexpr kNameKey[] = "name";
char constexpr kSizeKey[] = "size";
char constexpr kVersionKey[] = "version";
auto constexpr kTimeoutInSeconds = 5.0;
string SerializeCheckerData(LocalMapsInfo const & info)
{
auto mwmsArrayNode = base::NewJSONArray();
for (auto const & nameAndVersion : info.m_localMaps)
{
auto node = base::NewJSONObject();
ToJSONObject(*node, kNameKey, nameAndVersion.first);
ToJSONObject(*node, kVersionKey, nameAndVersion.second);
json_array_append_new(mwmsArrayNode.get(), node.release());
}
auto const root = base::NewJSONObject();
json_object_set_new(root.get(), kMwmsKey, mwmsArrayNode.release());
ToJSONObject(*root, kMaxVersionKey, info.m_currentDataVersion);
unique_ptr<char, JSONFreeDeleter> buffer(json_dumps(root.get(), JSON_COMPACT));
return buffer.get();
}
NameDiffInfoMap DeserializeResponse(string const & response, LocalMapsInfo::NameVersionMap const & nameVersionMap)
{
if (response.empty())
{
LOG(LERROR, ("Diff response shouldn't be empty."));
return {};
}
base::Json const json(response.c_str());
if (json.get() == nullptr)
return {};
auto const root = json_object_get(json.get(), kMwmsKey);
if (root == nullptr || !json_is_array(root))
return {};
auto const count = json_array_size(root);
if (count == 0 || count != nameVersionMap.size())
{
LOG(LERROR, ("Diff list size in response must be equal to mwm list size in request."));
return {};
}
NameDiffInfoMap diffs;
for (size_t i = 0; i < count; ++i)
{
auto const node = json_array_get(root, i);
if (!node)
{
LOG(LERROR, ("Incorrect server response."));
return {};
}
string name;
FromJSONObject(node, kNameKey, name);
int64_t size;
FromJSONObject(node, kSizeKey, size);
// Invalid size. The diff is not available.
if (size < 0)
continue;
if (nameVersionMap.find(name) == nameVersionMap.end())
{
LOG(LERROR, ("Incorrect country name in response:", name));
return {};
}
DiffInfo info(size, nameVersionMap.at(name));
diffs.emplace(std::move(name), std::move(info));
}
return diffs;
}
NameDiffInfoMap Load(LocalMapsInfo const & info)
{
if (info.m_localMaps.empty() || DIFF_LIST_URL[0] == 0)
return {};
platform::HttpClient request(DIFF_LIST_URL);
string const body = SerializeCheckerData(info);
ASSERT(!body.empty(), ());
request.SetBodyData(body, "application/json");
request.SetTimeout(kTimeoutInSeconds);
NameDiffInfoMap diffs;
if (request.RunHttpRequest() && !request.WasRedirected() && request.ErrorCode() == 200)
{
diffs = DeserializeResponse(request.ServerResponse(), info.m_localMaps);
}
else
{
ostringstream ost;
ost << "Request to diffs server failed. Code = " << request.ErrorCode()
<< ", redirection = " << request.WasRedirected();
LOG(LINFO, (ost.str()));
}
return diffs;
}
} // namespace
namespace storage
{
namespace diffs
{
// static
void Loader::Load(LocalMapsInfo && info, DiffsReceivedCallback && callback)
{
GetPlatform().RunTask(Platform::Thread::Network, [info = std::move(info), callback = std::move(callback)]()
{
auto result = ::Load(info);
GetPlatform().RunTask(Platform::Thread::Gui, [result = std::move(result), callback = std::move(callback)]() mutable
{ callback(std::move(result)); });
});
}
} // namespace diffs
} // namespace storage

View file

@ -0,0 +1,30 @@
#pragma once
#include "storage/diff_scheme/diff_types.hpp"
#include "storage/storage_defines.hpp"
#include <cstdint>
#include <functional>
#include <unordered_map>
namespace storage
{
namespace diffs
{
struct LocalMapsInfo final
{
using NameVersionMap = std::unordered_map<storage::CountryId, uint64_t>;
uint64_t m_currentDataVersion = 0;
NameVersionMap m_localMaps;
};
using DiffsReceivedCallback = std::function<void(diffs::NameDiffInfoMap && diffs)>;
class Loader final
{
public:
static void Load(LocalMapsInfo && info, DiffsReceivedCallback && callback);
};
} // namespace diffs
} // namespace storage

View file

@ -0,0 +1,31 @@
#pragma once
#include "storage/storage_defines.hpp"
#include <cstdint>
#include <string>
#include <unordered_map>
namespace storage
{
namespace diffs
{
// Status of the diffs data source as a whole.
enum class Status
{
NotAvailable,
Available
};
struct DiffInfo final
{
DiffInfo(uint64_t size, uint64_t version) : m_size(size), m_version(version) {}
uint64_t m_size;
uint64_t m_version;
bool m_isApplied = false;
};
using NameDiffInfoMap = std::unordered_map<storage::CountryId, DiffInfo>;
} // namespace diffs
} // namespace storage

View file

@ -0,0 +1,107 @@
#include "storage/diff_scheme/diffs_data_source.hpp"
#include <algorithm>
namespace
{
bool IsDiffsAvailable(storage::diffs::NameDiffInfoMap const & diffs)
{
return std::any_of(diffs.cbegin(), diffs.cend(), [](auto const & d) { return d.second.m_isApplied == false; });
}
} // namespace
namespace storage
{
namespace diffs
{
void DiffsDataSource::SetDiffInfo(NameDiffInfoMap && info)
{
CHECK_THREAD_CHECKER(m_threadChecker, ());
if (info.empty())
{
m_status = Status::NotAvailable;
}
else
{
m_diffs = std::move(info);
m_status = Status::Available;
}
}
Status DiffsDataSource::GetStatus() const
{
CHECK_THREAD_CHECKER(m_threadChecker, ());
return m_status;
}
bool DiffsDataSource::SizeFor(storage::CountryId const & countryId, uint64_t & size) const
{
CHECK_THREAD_CHECKER(m_threadChecker, ());
if (m_status != Status::Available)
return false;
auto const it = m_diffs.find(countryId);
if (it == m_diffs.cend())
return false;
size = it->second.m_size;
return true;
}
bool DiffsDataSource::SizeToDownloadFor(storage::CountryId const & countryId, uint64_t & size) const
{
CHECK_THREAD_CHECKER(m_threadChecker, ());
return WithNotAppliedDiff(countryId, [&size](DiffInfo const & info) { size = info.m_size; });
}
bool DiffsDataSource::VersionFor(storage::CountryId const & countryId, uint64_t & v) const
{
CHECK_THREAD_CHECKER(m_threadChecker, ());
return WithNotAppliedDiff(countryId, [&v](DiffInfo const & info) { v = info.m_version; });
}
bool DiffsDataSource::HasDiffFor(storage::CountryId const & countryId) const
{
CHECK_THREAD_CHECKER(m_threadChecker, ());
return WithNotAppliedDiff(countryId, [](DiffInfo const &) {});
}
void DiffsDataSource::MarkAsApplied(storage::CountryId const & countryId)
{
CHECK_THREAD_CHECKER(m_threadChecker, ());
auto it = m_diffs.find(countryId);
if (it == m_diffs.end())
return;
it->second.m_isApplied = true;
if (!IsDiffsAvailable(m_diffs))
m_status = Status::NotAvailable;
}
void DiffsDataSource::RemoveDiffForCountry(storage::CountryId const & countryId)
{
CHECK_THREAD_CHECKER(m_threadChecker, ());
m_diffs.erase(countryId);
if (m_diffs.empty() || !IsDiffsAvailable(m_diffs))
m_status = Status::NotAvailable;
}
void DiffsDataSource::AbortDiffScheme()
{
CHECK_THREAD_CHECKER(m_threadChecker, ());
m_status = Status::NotAvailable;
m_diffs.clear();
}
} // namespace diffs
} // namespace storage

View file

@ -0,0 +1,65 @@
#pragma once
#include "storage/diff_scheme/diff_types.hpp"
#include "storage/storage_defines.hpp"
#include "base/thread_checker.hpp"
#include <memory>
namespace storage
{
namespace diffs
{
class DiffsDataSource;
using DiffsSourcePtr = std::shared_ptr<diffs::DiffsDataSource>;
class DiffsDataSource final
{
public:
void SetDiffInfo(NameDiffInfoMap && info);
// If the diff is available, sets |size| to its size and returns true.
// Otherwise, returns false.
bool SizeFor(storage::CountryId const & countryId, uint64_t & size) const;
// Sets |size| to how many bytes are left for the diff to be downloaded for |countryId|
// or 0 if there is no diff available or it has already been downloaded.
// This method may overestimate because it does not account for the possibility
// of resuming an old download, i.e. the return value is either 0 or the diff size.
// Returns true iff the diff is available.
bool SizeToDownloadFor(storage::CountryId const & countryId, uint64_t & size) const;
bool VersionFor(storage::CountryId const & countryId, uint64_t & version) const;
// Checks whether the diff for |countryId| is available for download or
// has been downloaded.
bool HasDiffFor(storage::CountryId const & countryId) const;
void MarkAsApplied(storage::CountryId const & countryId);
void RemoveDiffForCountry(storage::CountryId const & countryId);
void AbortDiffScheme();
Status GetStatus() const;
private:
template <typename Fn>
bool WithNotAppliedDiff(storage::CountryId const & countryId, Fn && fn) const
{
if (m_status != Status::Available)
return false;
auto const it = m_diffs.find(countryId);
if (it == m_diffs.cend() || it->second.m_isApplied)
return false;
fn(it->second);
return true;
}
ThreadChecker m_threadChecker;
Status m_status = Status::NotAvailable;
NameDiffInfoMap m_diffs;
};
} // namespace diffs
} // namespace storage