/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#pragma once
#include
#include
namespace ams::fssystem {
class IndirectStorage : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable {
NON_COPYABLE(IndirectStorage);
NON_MOVEABLE(IndirectStorage);
public:
static constexpr s32 StorageCount = 2;
static constexpr size_t NodeSize = 16_KB;
using IAllocator = MemoryResource;
struct Entry {
u8 virt_offset[sizeof(s64)];
u8 phys_offset[sizeof(s64)];
s32 storage_index;
void SetVirtualOffset(const s64 &ofs) {
std::memcpy(this->virt_offset, std::addressof(ofs), sizeof(s64));
}
s64 GetVirtualOffset() const {
s64 offset;
std::memcpy(std::addressof(offset), this->virt_offset, sizeof(s64));
return offset;
}
void SetPhysicalOffset(const s64 &ofs) {
std::memcpy(this->phys_offset, std::addressof(ofs), sizeof(s64));
}
s64 GetPhysicalOffset() const {
s64 offset;
std::memcpy(std::addressof(offset), this->phys_offset, sizeof(s64));
return offset;
}
};
static_assert(util::is_pod::value);
static_assert(sizeof(Entry) == 0x14);
struct EntryData {
s64 virt_offset;
s64 phys_offset;
s32 storage_index;
void Set(const Entry &entry) {
this->virt_offset = entry.GetVirtualOffset();
this->phys_offset = entry.GetPhysicalOffset();
this->storage_index = entry.storage_index;
}
};
static_assert(util::is_pod::value);
private:
struct ContinuousReadingEntry {
static constexpr size_t FragmentSizeMax = 4_KB;
IndirectStorage::Entry entry;
s64 GetVirtualOffset() const {
return this->entry.GetVirtualOffset();
}
s64 GetPhysicalOffset() const {
return this->entry.GetPhysicalOffset();
}
bool IsFragment() const {
return this->entry.storage_index != 0;
}
};
static_assert(util::is_pod::value);
public:
static constexpr s64 QueryHeaderStorageSize() {
return BucketTree::QueryHeaderStorageSize();
}
static constexpr s64 QueryNodeStorageSize(s32 entry_count) {
return BucketTree::QueryNodeStorageSize(NodeSize, sizeof(Entry), entry_count);
}
static constexpr s64 QueryEntryStorageSize(s32 entry_count) {
return BucketTree::QueryEntryStorageSize(NodeSize, sizeof(Entry), entry_count);
}
private:
BucketTree m_table;
fs::SubStorage m_data_storage[StorageCount];
public:
IndirectStorage() : m_table(), m_data_storage() { /* ... */ }
virtual ~IndirectStorage() { this->Finalize(); }
Result Initialize(IAllocator *allocator, fs::SubStorage table_storage);
void Finalize();
bool IsInitialized() const { return m_table.IsInitialized(); }
Result Initialize(IAllocator *allocator, fs::SubStorage node_storage, fs::SubStorage entry_storage, s32 entry_count) {
return m_table.Initialize(allocator, node_storage, entry_storage, NodeSize, sizeof(Entry), entry_count);
}
void SetStorage(s32 idx, fs::SubStorage storage) {
AMS_ASSERT(0 <= idx && idx < StorageCount);
m_data_storage[idx] = storage;
}
template
void SetStorage(s32 idx, T storage, s64 offset, s64 size) {
AMS_ASSERT(0 <= idx && idx < StorageCount);
m_data_storage[idx] = fs::SubStorage(storage, offset, size);
}
Result GetEntryList(Entry *out_entries, s32 *out_entry_count, s32 entry_count, s64 offset, s64 size);
virtual Result Read(s64 offset, void *buffer, size_t size) override;
virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override;
virtual Result GetSize(s64 *out) override {
AMS_ASSERT(out != nullptr);
BucketTree::Offsets offsets;
R_TRY(m_table.GetOffsets(std::addressof(offsets)));
*out = offsets.end_offset;
R_SUCCEED();
}
virtual Result Flush() override {
R_SUCCEED();
}
virtual Result Write(s64 offset, const void *buffer, size_t size) override {
AMS_UNUSED(offset, buffer, size);
R_THROW(fs::ResultUnsupportedOperationInIndirectStorageA());
}
virtual Result SetSize(s64 size) override {
AMS_UNUSED(size);
R_THROW(fs::ResultUnsupportedOperationInIndirectStorageB());
}
protected:
BucketTree &GetEntryTable() { return m_table; }
fs::SubStorage &GetDataStorage(s32 index) {
AMS_ASSERT(0 <= index && index < StorageCount);
return m_data_storage[index];
}
template
Result OperatePerEntry(s64 offset, s64 size, F func);
};
}