mirror of
https://github.com/torlando-tech/pyxis.git
synced 2026-10-08 07:17:34 +00:00
Raise MapTileStore::PATH_CAPACITY from 64 to 80 so the mount buffer (PATH_CAPACITY + 4) holds the 80-character mounted tile path produced by a maximum-length (31-char) pack ID with full-width tile coordinates. Pre-fix, any tile with a two-digit x or y returned INVALID_ARGUMENT from the mount-prefix snprintf, which MapTilePack maps to IO_ERROR and the UI shows as 'Tile I/O error' even though the file exists and is intact. The host regression test added in the prior commit now passes under both strict C++11 and ASan/UBSan; reverting this change fails it.
158 lines
5.5 KiB
C++
158 lines
5.5 KiB
C++
// Copyright (c) 2026 Pyxis contributors
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
#ifndef HARDWARE_TDECK_MAP_TILE_STORE_H
|
|
#define HARDWARE_TDECK_MAP_TILE_STORE_H
|
|
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
|
|
namespace Hardware {
|
|
namespace TDeck {
|
|
|
|
enum class TileStoreResult : std::uint8_t {
|
|
OK,
|
|
MISS,
|
|
INVALID_KEY,
|
|
INVALID_ARGUMENT,
|
|
STORAGE_UNAVAILABLE,
|
|
IO_ERROR,
|
|
INVALID_PNG,
|
|
TOO_LARGE,
|
|
QUOTA_EXCEEDED,
|
|
INDEX_FULL,
|
|
INDEX_MISMATCH,
|
|
NOT_INITIALIZED,
|
|
BUSY
|
|
};
|
|
|
|
struct TileKey {
|
|
std::uint8_t zoom;
|
|
std::uint32_t x;
|
|
std::uint32_t y;
|
|
};
|
|
|
|
struct TileStoreConfig {
|
|
std::uint16_t max_entries;
|
|
std::uint32_t byte_quota;
|
|
std::uint32_t max_tile_bytes;
|
|
};
|
|
|
|
/** Storage boundary used by the portable fixed-capacity cache core. */
|
|
class MapTileStorage {
|
|
public:
|
|
virtual ~MapTileStorage() {}
|
|
virtual bool isAvailable() const = 0;
|
|
virtual TileStoreResult beginRead(const char* name, std::uint32_t& size) = 0;
|
|
virtual TileStoreResult readChunk(std::uint8_t* output, std::size_t capacity, std::size_t& count) = 0;
|
|
virtual void endRead() = 0;
|
|
virtual TileStoreResult beginWrite(const char* name) = 0;
|
|
virtual TileStoreResult writeChunk(const std::uint8_t* data, std::size_t size, std::size_t& written) = 0;
|
|
virtual TileStoreResult commitWrite() = 0;
|
|
virtual void abortWrite() = 0;
|
|
virtual TileStoreResult remove(const char* name) = 0;
|
|
virtual TileStoreResult rename(const char* from, const char* to) = 0;
|
|
virtual TileStoreResult stat(const char* name, std::uint32_t& size) = 0;
|
|
virtual TileStoreResult beginList() = 0;
|
|
virtual TileStoreResult nextList(char* name, std::size_t capacity, bool& done) = 0;
|
|
virtual void endList() = 0;
|
|
};
|
|
|
|
/**
|
|
* Bounded, allocation-free offline slippy-map tile store.
|
|
*
|
|
* initialize() reconstructs a fixed index from the owned SD namespace. It
|
|
* rejects malformed/unaccounted names, over-capacity media, and over-quota
|
|
* media rather than allowing pre-existing files to escape accounting. LRU
|
|
* order after reboot is the canonical TileKey order and is therefore
|
|
* deterministic; successful reads/writes advance the in-memory sequence.
|
|
*/
|
|
class MapTileStore {
|
|
public:
|
|
static const std::uint8_t MAX_ZOOM = 22U;
|
|
// Mount prefix ("/sd" + NUL) plus the longest device-relative path:
|
|
// /pyxis-map/packs/<31-char pack ID>/tiles/<z>/<x>/<y>.png with
|
|
// full-width tile coordinates (80 characters, 81 with NUL). 80 gives an
|
|
// 84-byte mount buffer; MapTilePack::PATH_CAPACITY (80) is sized for
|
|
// the device-relative form and matches this bound.
|
|
static const std::size_t PATH_CAPACITY = 80U;
|
|
static const std::uint16_t HARD_MAX_ENTRIES = 128U;
|
|
|
|
MapTileStore(MapTileStorage& storage, const TileStoreConfig& config);
|
|
|
|
TileStoreResult initialize();
|
|
static bool isValidKey(const TileKey& key);
|
|
static TileStoreResult canonicalPath(const TileKey& key, char* output, std::size_t capacity);
|
|
|
|
TileStoreResult beginGet(const TileKey& key, std::uint32_t& size);
|
|
TileStoreResult readGetChunk(std::uint8_t* output, std::size_t capacity, std::size_t& count);
|
|
void endGet();
|
|
TileStoreResult removeTile(const TileKey& key);
|
|
|
|
TileStoreResult beginPut(const TileKey& key);
|
|
TileStoreResult writePutChunk(const std::uint8_t* data, std::size_t size);
|
|
TileStoreResult finishPut();
|
|
void abortPut();
|
|
|
|
std::uint16_t entryCount() const { return entry_count_; }
|
|
std::uint32_t totalBytes() const { return total_bytes_; }
|
|
std::uint32_t maxTileBytes() const { return config_.max_tile_bytes; }
|
|
bool isAvailable() const { return storage_.isAvailable(); }
|
|
static std::size_t ramBytes() { return sizeof(MapTileStore); }
|
|
|
|
private:
|
|
struct Entry {
|
|
TileKey key;
|
|
std::uint32_t size;
|
|
std::uint64_t sequence;
|
|
std::uint8_t recovery_flags;
|
|
};
|
|
|
|
enum RecoveryFlag {
|
|
HAS_LIVE = 1,
|
|
HAS_TEMP = 2,
|
|
HAS_BACKUP = 4,
|
|
HAS_EVICT = 8
|
|
};
|
|
|
|
MapTileStorage& storage_;
|
|
TileStoreConfig config_;
|
|
Entry entries_[HARD_MAX_ENTRIES];
|
|
std::uint16_t entry_count_;
|
|
std::uint32_t total_bytes_;
|
|
std::uint64_t next_sequence_;
|
|
bool initialized_;
|
|
bool read_open_;
|
|
bool write_open_;
|
|
TileKey put_key_;
|
|
char put_live_[PATH_CAPACITY];
|
|
char put_temp_[PATH_CAPACITY];
|
|
char put_backup_[PATH_CAPACITY];
|
|
std::uint8_t png_header_[24];
|
|
std::size_t png_header_count_;
|
|
std::uint32_t put_size_;
|
|
|
|
static bool sameKey(const TileKey& a, const TileKey& b);
|
|
static bool keyLess(const TileKey& a, const TileKey& b);
|
|
static TileStoreResult parseOwnedPath(const char* name, TileKey& key, std::uint8_t& flag);
|
|
int findEntry(const TileKey& key) const;
|
|
void removeEntry(std::uint16_t index);
|
|
TileStoreResult validatePathHeader(const char* path, std::uint32_t& size);
|
|
TileStoreResult validateLiveHeader(const Entry& entry);
|
|
TileStoreResult recoverIndex();
|
|
TileStoreResult recoverEvictionTransaction();
|
|
TileStoreResult planEviction(const TileKey& key, std::uint32_t new_size,
|
|
TileKey* victims, std::uint16_t& victim_count) const;
|
|
TileStoreResult writeEvictionTransaction(const TileKey& key, bool duplicate,
|
|
const TileKey* victims, std::uint16_t victim_count);
|
|
TileStoreResult evictFor(const TileKey* victims, std::uint16_t victim_count);
|
|
void cleanupEvicted(const TileKey* victims, std::uint16_t victim_count);
|
|
bool validPngHeader() const;
|
|
void failPut();
|
|
};
|
|
|
|
} // namespace TDeck
|
|
} // namespace Hardware
|
|
|
|
#endif
|