mirror of
https://github.com/vicliu624/trail-mate.git
synced 2026-11-04 19:27:28 +00:00
254 lines
11 KiB
C++
254 lines
11 KiB
C++
#include "ui_map_runtime/map_poi/poi_types.h"
|
|
#include "ui_map_runtime/map_tiles/native_pixel_buffer.h"
|
|
#include "ui_map_runtime/map_tiles/tmap_map_tile_source.h"
|
|
#include <cassert>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <memory>
|
|
#include <vector>
|
|
#ifdef _WIN32
|
|
#include <malloc.h>
|
|
#endif
|
|
|
|
using namespace ui::map_tiles;
|
|
void* alignedAllocate(size_t alignment, size_t bytes)
|
|
{
|
|
#ifdef _WIN32
|
|
return _aligned_malloc(bytes, alignment);
|
|
#else
|
|
void* output = nullptr;
|
|
return posix_memalign(&output, std::max(alignment, sizeof(void*)), bytes) ? nullptr : output;
|
|
#endif
|
|
}
|
|
void alignedFree(void* memory)
|
|
{
|
|
#ifdef _WIN32
|
|
_aligned_free(memory);
|
|
#else
|
|
std::free(memory);
|
|
#endif
|
|
}
|
|
class Storage final : public TmapStorage
|
|
{
|
|
public:
|
|
const char* paths[2];
|
|
unsigned at = 0, opens = 0;
|
|
uint32_t media = 1;
|
|
size_t memory = 0, allocations = 0;
|
|
FILE* file = nullptr;
|
|
uint64_t length = 0;
|
|
bool fail_allocation = false, busy_read = false;
|
|
bool corrupt_header = false, corrupt_directory = false, corrupt_pixel = false;
|
|
unsigned package_limit = 2;
|
|
explicit Storage(char** argv) : paths{argv[1], argv[2]} {}
|
|
void* allocate(size_t bytes, size_t alignment) override
|
|
{
|
|
if (fail_allocation) return nullptr;
|
|
auto* output = alignedAllocate(alignment, bytes);
|
|
if (output)
|
|
{
|
|
memory += bytes;
|
|
++allocations;
|
|
}
|
|
return output;
|
|
}
|
|
void release(void* output) override
|
|
{
|
|
alignedFree(output);
|
|
--allocations;
|
|
}
|
|
uint32_t session() const override { return media; }
|
|
tmap::Status nextPackage(char* output, size_t capacity) override
|
|
{
|
|
if (at == package_limit) return tmap::Status::Missing;
|
|
if (std::strlen(paths[at % 2]) >= capacity) return tmap::Status::Invalid;
|
|
std::strcpy(output, paths[at++ % 2]);
|
|
return tmap::Status::Ok;
|
|
}
|
|
tmap::Status openPackage(const char* path) override
|
|
{
|
|
closePackage();
|
|
file = std::fopen(path, "rb");
|
|
if (!file) return tmap::Status::IoError;
|
|
#ifdef _WIN32
|
|
_fseeki64(file, 0, SEEK_END);
|
|
length = _ftelli64(file);
|
|
#else
|
|
fseeko(file, 0, SEEK_END);
|
|
length = ftello(file);
|
|
#endif
|
|
++opens;
|
|
return tmap::Status::Ok;
|
|
}
|
|
uint64_t size() const override { return length; }
|
|
void closePackage() override
|
|
{
|
|
if (file) std::fclose(file);
|
|
file = nullptr;
|
|
length = 0;
|
|
}
|
|
void resetEnumeration() override { at = 0; }
|
|
tmap::Status readAt(uint64_t offset, uint8_t* output, size_t bytes) override
|
|
{
|
|
if (busy_read)
|
|
{
|
|
busy_read = false;
|
|
return tmap::Status::Busy;
|
|
}
|
|
#ifdef _WIN32
|
|
if (_fseeki64(file, offset, SEEK_SET)) return tmap::Status::IoError;
|
|
#else
|
|
if (fseeko(file, offset, SEEK_SET)) return tmap::Status::IoError;
|
|
#endif
|
|
if (std::fread(output, 1, bytes, file) != bytes) return tmap::Status::IoError;
|
|
if ((corrupt_header && offset == 0) || (corrupt_directory && offset == 4096) ||
|
|
(corrupt_pixel && bytes == 131072)) output[0] ^= 1;
|
|
return tmap::Status::Ok;
|
|
}
|
|
};
|
|
int main(int argc, char** argv)
|
|
{
|
|
if (argc != 3) return 2;
|
|
Storage storage(argv);
|
|
TmapMapTileSource source(storage);
|
|
auto* pixels = static_cast<uint8_t*>(alignedAllocate(16, 256 * 1024));
|
|
assert(pixels);
|
|
const MapTileRef world{MapTileLayer::Osm, 7, 105, 48};
|
|
const MapTileRef china{MapTileLayer::Osm, 12, 3216, 1753};
|
|
storage.fail_allocation = true;
|
|
assert(source.read(world, pixels, 256 * 1024).status == MapTileReadStatus::Error);
|
|
assert(storage.allocations == 0);
|
|
storage.fail_allocation = false;
|
|
storage.busy_read = true;
|
|
assert(source.read(world, pixels, 256 * 1024).status == MapTileReadStatus::RetryLater);
|
|
auto first = source.read(world, pixels, 256 * 1024);
|
|
assert(first.status == MapTileReadStatus::Ready && first.format == MapTileFormat::Rgb565 && first.size == 131072);
|
|
assert(tmap::Reader::crc32c(pixels, first.size) == 0x3ef54c05);
|
|
assert(source.packageCount() == 2);
|
|
const auto bytes = source.allocatedBytes();
|
|
std::printf("catalog bytes=%u allocations=%u\n", unsigned(bytes), unsigned(storage.allocations));
|
|
std::fflush(stdout);
|
|
// Catalog metadata includes three retained annotation continuation slots.
|
|
// Budget that metadata separately from the unchanged v1 reader workspace.
|
|
assert(bytes < sizeof(tmap::Workspace) + 12 * 1024 && storage.allocations == 2);
|
|
const auto opens = storage.opens;
|
|
assert(source.read(world, pixels, 256 * 1024).status == MapTileReadStatus::Ready);
|
|
assert(storage.opens == opens);
|
|
auto second = source.read(china, pixels, 256 * 1024);
|
|
assert(second.status == MapTileReadStatus::Ready && tmap::Reader::crc32c(pixels, second.size) == 0xa4e01293);
|
|
const auto located = source.lookup(china);
|
|
assert(located.status == MapTileStatus::Available && located.format == MapTileFormat::Rgb565 && located.size == 131072);
|
|
auto poi = china;
|
|
poi.layer = MapTileLayer::Poi;
|
|
auto annotations = source.read(poi, pixels, 256 * 1024);
|
|
unsigned annotation_steps = 0;
|
|
while (annotation_steps++ < 2000 &&
|
|
(annotations.status == MapTileReadStatus::RetryLater ||
|
|
(annotations.status == MapTileReadStatus::Ready &&
|
|
reinterpret_cast<const ui::map_poi::TileHeader*>(pixels)->partial)))
|
|
annotations = source.read(poi, pixels, 256 * 1024);
|
|
assert(annotation_steps < 2000);
|
|
const auto poi_info = source.lookup(poi);
|
|
assert(poi_info.status == MapTileStatus::Available && poi_info.format == MapTileFormat::PoiRecords && poi_info.size == 0);
|
|
assert(annotations.status == MapTileReadStatus::Ready && annotations.format == MapTileFormat::PoiRecords);
|
|
assert(ui::map_poi::validPayload(pixels, annotations.size));
|
|
const auto* header = reinterpret_cast<const ui::map_poi::TileHeader*>(pixels);
|
|
assert(header->count == 42);
|
|
assert(ui::map_poi::payloadRecords(pixels)[0].kind == ui::map::AnnotationKind::Place);
|
|
assert(std::strcmp(ui::map_poi::payloadRecords(pixels)[0].name, u8"昆明市") == 0);
|
|
++storage.media;
|
|
assert(source.read(world, pixels, 256 * 1024).status == MapTileReadStatus::Ready);
|
|
assert(source.allocatedBytes() == bytes && storage.allocations == 2);
|
|
source.reset();
|
|
assert(storage.allocations == 0 && source.allocatedBytes() == 0 && !storage.file);
|
|
storage.corrupt_header = true;
|
|
assert(source.read(world, pixels, 262144).status == MapTileReadStatus::Invalid);
|
|
source.reset();
|
|
storage.corrupt_header = false;
|
|
storage.corrupt_directory = true;
|
|
assert(source.read(world, pixels, 262144).status == MapTileReadStatus::Invalid);
|
|
source.reset();
|
|
storage.corrupt_directory = false;
|
|
storage.corrupt_pixel = true;
|
|
assert(source.read(world, pixels, 262144).status == MapTileReadStatus::Invalid);
|
|
source.reset();
|
|
storage.corrupt_pixel = false;
|
|
storage.package_limit = 257;
|
|
MapTileReadResult overflow{};
|
|
unsigned catalog_steps = 0;
|
|
do
|
|
{
|
|
overflow = source.read(world, pixels, 262144);
|
|
++catalog_steps;
|
|
} while (overflow.status == MapTileReadStatus::RetryLater && catalog_steps < 40);
|
|
assert(overflow.status == MapTileReadStatus::Invalid && source.packageCount() == 256);
|
|
// The first block is already counted in bytes; 256 packages add 15 more
|
|
// blocks. Bound metadata explicitly rather than an outdated total constant.
|
|
const auto block_budget = 16 * (sizeof(tmap::Package) + 13 * sizeof(uint32_t) + 192 + 16) + 16;
|
|
assert(source.allocatedBytes() < bytes + 15 * block_budget);
|
|
source.reset();
|
|
assert(storage.allocations == 0);
|
|
storage.package_limit = 2;
|
|
alignedFree(pixels);
|
|
|
|
assert(storage.openPackage(argv[2]) == tmap::Status::Ok);
|
|
auto workspace = std::make_unique<tmap::Workspace>();
|
|
auto cursor = std::make_unique<tmap::SearchCursor>();
|
|
tmap::Reader reader(*workspace);
|
|
assert(reader.open(storage) == tmap::Status::Ok);
|
|
auto countResult = [](void* count, const tmap::Poi&, tmap::SearchMode)
|
|
{ ++*static_cast<unsigned*>(count); return true; };
|
|
for (auto mode : {tmap::SearchMode::Exact, tmap::SearchMode::Prefix, tmap::SearchMode::Substring})
|
|
{
|
|
unsigned matches = 0;
|
|
assert(reader.beginSearch(u8"昆明市", std::strlen(u8"昆明市"), mode, *cursor) == tmap::Status::Ok);
|
|
storage.busy_read = true;
|
|
// Drop cached pages without losing the file binding, to force Busy at initialization.
|
|
for (auto& page : workspace->pages) page.valid = false;
|
|
assert(reader.searchStep(*cursor, 1, countResult, &matches) == tmap::Status::Busy);
|
|
unsigned steps = 0;
|
|
tmap::Status status;
|
|
do
|
|
{
|
|
status = reader.searchStep(*cursor, 1, countResult, &matches);
|
|
++steps;
|
|
} while (status == tmap::Status::More && steps < 2000);
|
|
assert(status == tmap::Status::Ok && matches > 0 && steps > 1);
|
|
}
|
|
unsigned cancelled = 0;
|
|
assert(reader.beginSearch(u8"昆明", std::strlen(u8"昆明"), tmap::SearchMode::Substring, *cursor) == tmap::Status::Ok);
|
|
auto stopResult = [](void* count, const tmap::Poi&, tmap::SearchMode)
|
|
{ ++*static_cast<unsigned*>(count); return false; };
|
|
auto status = reader.searchStep(*cursor, 64, stopResult, &cancelled);
|
|
assert(status == tmap::Status::Cancelled && cancelled == 1);
|
|
assert(reader.beginSearch("__absent_tmap_name__", 20, tmap::SearchMode::Exact, *cursor) == tmap::Status::Ok);
|
|
unsigned absent = 0;
|
|
assert(reader.searchStep(*cursor, 64, countResult, &absent) == tmap::Status::Ok && absent == 0);
|
|
reader.close();
|
|
storage.closePackage();
|
|
|
|
std::atomic<size_t> used{0};
|
|
auto* leased = NativePixelBuffer::create(131072, used, 140000, alignedAllocate, alignedFree);
|
|
assert(leased && used > 131072);
|
|
assert(!NativePixelBuffer::create(131072, used, 140000, alignedAllocate, alignedFree));
|
|
NativePixelBuffer::retain(leased);
|
|
NativePixelBuffer::release(leased);
|
|
assert(used > 0);
|
|
NativePixelBuffer::release(leased);
|
|
assert(used == 0);
|
|
auto* rgba = NativePixelBuffer::create(262144, used, 300000, alignedAllocate, alignedFree);
|
|
assert(rgba);
|
|
std::memset(rgba, 0, 262144);
|
|
rgba[0] = 1;
|
|
rgba[1] = 2;
|
|
rgba[2] = 3;
|
|
rgba[3] = 4;
|
|
NativePixelBuffer::convertRgbaToBgra(rgba, 262144);
|
|
NativePixelBuffer::convertRgbaToBgra(rgba, 262144);
|
|
assert(rgba[0] == 3 && rgba[1] == 2 && rgba[2] == 1 && rgba[3] == 4);
|
|
NativePixelBuffer::release(rgba);
|
|
assert(used == 0);
|
|
std::printf("source_probe: world/china/annotations/busy/session/ownership/CRC/capacity/search/cancel PASS; catalog_bytes=%u\n", static_cast<unsigned>(bytes));
|
|
}
|