Revert "hoc-clk: add live vdd2, live boost clock and basic pwm dimming"
This reverts commit 15b7df8ef1.
This commit is contained in:
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere/kvdb/kvdb_auto_buffer.hpp>
|
||||
|
||||
namespace ams::kvdb {
|
||||
|
||||
/* Functionality for parsing/generating a key value archive. */
|
||||
class ArchiveReader {
|
||||
private:
|
||||
AutoBuffer &m_buffer;
|
||||
size_t m_offset;
|
||||
public:
|
||||
ArchiveReader(AutoBuffer &b) : m_buffer(b), m_offset(0) { /* ... */ }
|
||||
private:
|
||||
Result Peek(void *dst, size_t size);
|
||||
Result Read(void *dst, size_t size);
|
||||
public:
|
||||
Result ReadEntryCount(size_t *out);
|
||||
Result GetEntrySize(size_t *out_key_size, size_t *out_value_size);
|
||||
Result ReadEntry(void *out_key, size_t key_size, void *out_value, size_t value_size);
|
||||
};
|
||||
|
||||
class ArchiveWriter {
|
||||
private:
|
||||
AutoBuffer &m_buffer;
|
||||
size_t m_offset;
|
||||
public:
|
||||
ArchiveWriter(AutoBuffer &b) : m_buffer(b), m_offset(0) { /* ... */ }
|
||||
private:
|
||||
Result Write(const void *src, size_t size);
|
||||
public:
|
||||
void WriteHeader(size_t entry_count);
|
||||
void WriteEntry(const void *key, size_t key_size, const void *value, size_t value_size);
|
||||
};
|
||||
|
||||
class ArchiveSizeHelper {
|
||||
private:
|
||||
size_t m_size;
|
||||
public:
|
||||
ArchiveSizeHelper();
|
||||
|
||||
void AddEntry(size_t key_size, size_t value_size);
|
||||
|
||||
size_t GetSize() const {
|
||||
return m_size;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
|
||||
namespace ams::kvdb {
|
||||
|
||||
class AutoBuffer {
|
||||
NON_COPYABLE(AutoBuffer);
|
||||
private:
|
||||
u8 *m_buffer;
|
||||
size_t m_size;
|
||||
public:
|
||||
AutoBuffer() : m_buffer(nullptr), m_size(0) { /* ... */ }
|
||||
|
||||
~AutoBuffer() {
|
||||
this->Reset();
|
||||
}
|
||||
|
||||
AutoBuffer(AutoBuffer &&rhs) {
|
||||
m_buffer = rhs.m_buffer;
|
||||
m_size = rhs.m_size;
|
||||
rhs.m_buffer = nullptr;
|
||||
rhs.m_size = 0;
|
||||
}
|
||||
|
||||
AutoBuffer &operator=(AutoBuffer &&rhs) {
|
||||
AutoBuffer(std::move(rhs)).Swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Swap(AutoBuffer &rhs) {
|
||||
std::swap(m_buffer, rhs.m_buffer);
|
||||
std::swap(m_size, rhs.m_size);
|
||||
}
|
||||
|
||||
void Reset() {
|
||||
if (m_buffer != nullptr) {
|
||||
delete[] m_buffer;
|
||||
m_buffer = nullptr;
|
||||
m_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
u8 *Get() const {
|
||||
return m_buffer;
|
||||
}
|
||||
|
||||
size_t GetSize() const {
|
||||
return m_size;
|
||||
}
|
||||
|
||||
Result Initialize(size_t size) {
|
||||
/* Check that we're not already initialized. */
|
||||
AMS_ABORT_UNLESS(m_buffer == nullptr);
|
||||
|
||||
/* Allocate a buffer. */
|
||||
m_buffer = new (std::nothrow) u8[size];
|
||||
R_UNLESS(m_buffer != nullptr, kvdb::ResultAllocationFailed());
|
||||
|
||||
m_size = size;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result Initialize(const void *buf, size_t size) {
|
||||
/* Create a new buffer of the right size. */
|
||||
R_TRY(this->Initialize(size));
|
||||
|
||||
/* Copy the input data in. */
|
||||
std::memcpy(m_buffer, buf, size);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
|
||||
namespace ams::kvdb {
|
||||
|
||||
/* Represents a string with a backing buffer of N bytes. */
|
||||
template<size_t N>
|
||||
class BoundedString {
|
||||
static_assert(N > 0, "BoundedString requires non-zero backing buffer!");
|
||||
private:
|
||||
char m_buffer[N];
|
||||
public:
|
||||
/* Constructors. */
|
||||
constexpr BoundedString() {
|
||||
m_buffer[0] = 0;
|
||||
}
|
||||
|
||||
explicit constexpr BoundedString(const char *s) {
|
||||
AMS_ABORT_UNLESS(static_cast<size_t>(util::Strnlen(s, N)) < N);
|
||||
util::Strlcpy(m_buffer, s, N);
|
||||
}
|
||||
|
||||
/* Static constructors. */
|
||||
static constexpr BoundedString<N> Make(const char *s) {
|
||||
return BoundedString<N>(s);
|
||||
}
|
||||
|
||||
static BoundedString<N> MakeFormat(const char *format, ...) __attribute__((format (printf, 1, 2))) {
|
||||
BoundedString<N> string;
|
||||
|
||||
std::va_list vl;
|
||||
va_start(vl, format);
|
||||
AMS_ABORT_UNLESS(static_cast<size_t>(util::VSNPrintf(string.m_buffer, N, format, vl)) < N);
|
||||
va_end(vl);
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
/* Getters. */
|
||||
constexpr size_t GetLength() const {
|
||||
return util::Strnlen(m_buffer, N);
|
||||
}
|
||||
|
||||
constexpr const char *Get() const {
|
||||
return m_buffer;
|
||||
}
|
||||
|
||||
constexpr operator const char *() const {
|
||||
return m_buffer;
|
||||
}
|
||||
|
||||
/* Assignment. */
|
||||
constexpr BoundedString<N> &Assign(const char *s) {
|
||||
AMS_ABORT_UNLESS(static_cast<size_t>(util::Strnlen(s, N)) < N);
|
||||
util::Strlcpy(m_buffer, s, N);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
BoundedString<N> &AssignFormat(const char *format, ...) __attribute__((format (printf, 2, 3))) {
|
||||
std::va_list vl;
|
||||
va_start(vl, format);
|
||||
AMS_ABORT_UNLESS(static_cast<size_t>(util::VSNPrintf(m_buffer, N, format, vl)) < N);
|
||||
va_end(vl);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/* Append to existing. */
|
||||
BoundedString<N> &Append(const char *s) {
|
||||
const size_t length = GetLength();
|
||||
AMS_ABORT_UNLESS(length + static_cast<size_t>(util::Strnlen(s, N)) < N);
|
||||
std::strncat(m_buffer, s, N - length - 1);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
BoundedString<N> &Append(char c) {
|
||||
const size_t length = GetLength();
|
||||
AMS_ABORT_UNLESS(length + 1 < N);
|
||||
m_buffer[length] = c;
|
||||
m_buffer[length + 1] = 0;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
BoundedString<N> &AppendFormat(const char *format, ...) __attribute__((format (printf, 2, 3))) {
|
||||
const size_t length = GetLength();
|
||||
|
||||
std::va_list vl;
|
||||
va_start(vl, format);
|
||||
AMS_ABORT_UNLESS(static_cast<size_t>(util::VSNPrintf(m_buffer + length, N - length, format, vl)) < static_cast<size_t>(N - length));
|
||||
va_end(vl);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/* Substring utilities. */
|
||||
void GetSubString(char *dst, size_t dst_size, size_t offset, size_t length) const {
|
||||
/* Make sure output buffer can hold the substring. */
|
||||
AMS_ABORT_UNLESS(offset + length <= GetLength());
|
||||
AMS_ABORT_UNLESS(dst_size > length);
|
||||
|
||||
/* Copy substring to dst. */
|
||||
std::strncpy(dst, m_buffer + offset, length);
|
||||
dst[length] = 0;
|
||||
}
|
||||
|
||||
BoundedString<N> MakeSubString(size_t offset, size_t length) const {
|
||||
BoundedString<N> string;
|
||||
GetSubString(string.m_buffer, N, offset, length);
|
||||
return string;
|
||||
}
|
||||
|
||||
/* Comparison. */
|
||||
constexpr bool Equals(const char *s, size_t offset = 0) const {
|
||||
if (std::is_constant_evaluated()) {
|
||||
return util::Strncmp(m_buffer + offset, s, N - offset) == 0;
|
||||
} else {
|
||||
return std::strncmp(m_buffer + offset, s, N - offset) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr bool operator==(const BoundedString<N> &rhs) const {
|
||||
return this->Equals(rhs.m_buffer);
|
||||
}
|
||||
|
||||
constexpr bool operator!=(const BoundedString<N> &rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
constexpr bool EqualsPostfix(const char *s) const {
|
||||
const size_t suffix_length = util::Strnlen(s, N);
|
||||
const size_t length = GetLength();
|
||||
return suffix_length <= length && this->Equals(s, length - suffix_length);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,382 +0,0 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere/fs/fs_filesystem.hpp>
|
||||
#include <stratosphere/fs/fs_file.hpp>
|
||||
#include <stratosphere/kvdb/kvdb_bounded_string.hpp>
|
||||
#include <stratosphere/kvdb/kvdb_file_key_value_store.hpp>
|
||||
|
||||
namespace ams::kvdb {
|
||||
|
||||
namespace impl {
|
||||
|
||||
template<class Key, size_t Capacity>
|
||||
class LruList {
|
||||
private:
|
||||
/* Subtypes. */
|
||||
struct LruHeader {
|
||||
u32 entry_count;
|
||||
};
|
||||
public:
|
||||
static constexpr size_t BufferSize = sizeof(Key) * Capacity;
|
||||
static constexpr size_t FileSize = sizeof(LruHeader) + BufferSize;
|
||||
using Path = FileKeyValueStore::Path;
|
||||
private:
|
||||
Path m_file_path;
|
||||
Key *m_keys;
|
||||
LruHeader m_header;
|
||||
public:
|
||||
static Result CreateNewList(const char *path) {
|
||||
/* Create new lru_list.dat. */
|
||||
R_TRY(fs::CreateFile(path, FileSize));
|
||||
|
||||
/* Open the file. */
|
||||
fs::FileHandle file;
|
||||
R_TRY(fs::OpenFile(std::addressof(file), path, fs::OpenMode_Write));
|
||||
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||
|
||||
/* Write new header with zero entries to the file. */
|
||||
LruHeader new_header = { .entry_count = 0, };
|
||||
R_TRY(fs::WriteFile(file, 0, std::addressof(new_header), sizeof(new_header), fs::WriteOption::Flush));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
private:
|
||||
void RemoveIndex(size_t i) {
|
||||
AMS_ABORT_UNLESS(i < this->GetCount());
|
||||
std::memmove(m_keys + i, m_keys + i + 1, sizeof(*m_keys) * (this->GetCount() - (i + 1)));
|
||||
this->DecrementCount();
|
||||
}
|
||||
|
||||
void IncrementCount() {
|
||||
m_header.entry_count++;
|
||||
}
|
||||
|
||||
void DecrementCount() {
|
||||
m_header.entry_count--;
|
||||
}
|
||||
public:
|
||||
LruList() : m_keys(nullptr), m_header() { /* ... */ }
|
||||
|
||||
Result Initialize(const char *path, void *buf, size_t size) {
|
||||
/* Only initialize once, and ensure we have sufficient memory. */
|
||||
AMS_ABORT_UNLESS(m_keys == nullptr);
|
||||
AMS_ABORT_UNLESS(size >= BufferSize);
|
||||
|
||||
/* Setup member variables. */
|
||||
m_keys = static_cast<Key *>(buf);
|
||||
m_file_path.Assign(path);
|
||||
std::memset(m_keys, 0, BufferSize);
|
||||
|
||||
/* Open file. */
|
||||
fs::FileHandle file;
|
||||
R_TRY(fs::OpenFile(std::addressof(file), m_file_path, fs::OpenMode_Read));
|
||||
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||
|
||||
/* Read header. */
|
||||
R_TRY(fs::ReadFile(file, 0, std::addressof(m_header), sizeof(m_header)));
|
||||
|
||||
/* Read entries. */
|
||||
R_TRY(fs::ReadFile(file, sizeof(m_header), m_keys, BufferSize));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result Save() {
|
||||
/* Open file. */
|
||||
fs::FileHandle file;
|
||||
R_TRY(fs::OpenFile(std::addressof(file), m_file_path, fs::OpenMode_Read));
|
||||
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||
|
||||
/* Write header. */
|
||||
R_TRY(fs::WriteFile(file, 0, std::addressof(m_header), sizeof(m_header), fs::WriteOption::None));
|
||||
|
||||
/* Write entries. */
|
||||
R_TRY(fs::WriteFile(file, sizeof(m_header), m_keys, BufferSize, fs::WriteOption::None));
|
||||
|
||||
/* Flush. */
|
||||
R_TRY(fs::FlushFile(file));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
size_t GetCount() const {
|
||||
return m_header.entry_count;
|
||||
}
|
||||
|
||||
bool IsEmpty() const {
|
||||
return this->GetCount() == 0;
|
||||
}
|
||||
|
||||
bool IsFull() const {
|
||||
return this->GetCount() >= Capacity;
|
||||
}
|
||||
|
||||
Key Get(size_t i) const {
|
||||
AMS_ABORT_UNLESS(i < this->GetCount());
|
||||
return m_keys[i];
|
||||
}
|
||||
|
||||
Key Peek() const {
|
||||
AMS_ABORT_UNLESS(!this->IsEmpty());
|
||||
return this->Get(0);
|
||||
}
|
||||
|
||||
void Push(const Key &key) {
|
||||
AMS_ABORT_UNLESS(!this->IsFull());
|
||||
m_keys[this->GetCount()] = key;
|
||||
this->IncrementCount();
|
||||
}
|
||||
|
||||
Key Pop() {
|
||||
AMS_ABORT_UNLESS(!this->IsEmpty());
|
||||
this->RemoveIndex(0);
|
||||
}
|
||||
|
||||
bool Remove(const Key &key) {
|
||||
const size_t count = this->GetCount();
|
||||
|
||||
/* Iterate over the list, removing the last entry that matches the key. */
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
if (m_keys[count - 1 - i] == key) {
|
||||
this->RemoveIndex(count - 1 - i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Contains(const Key &key) const {
|
||||
const size_t count = this->GetCount();
|
||||
|
||||
/* Iterate over the list, checking to see if we have the key. */
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
if (m_keys[count - 1 - i] == key) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Update(const Key &key) {
|
||||
if (this->Remove(key)) {
|
||||
this->Push(key);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template<class Key, size_t Capacity>
|
||||
class FileKeyValueCache {
|
||||
static_assert(util::is_pod<Key>::value, "FileKeyValueCache Key must be pod!");
|
||||
static_assert(sizeof(Key) <= FileKeyValueStore::MaxKeySize, "FileKeyValueCache Key is too big!");
|
||||
public:
|
||||
using LeastRecentlyUsedList = impl::LruList<Key, Capacity>;
|
||||
/* Note: Nintendo code in NS uses Path = BoundedString<0x180> here. */
|
||||
/* It's unclear why, since they use 0x300 everywhere else. */
|
||||
/* We'll just use 0x300, since it shouldn't make a difference, */
|
||||
/* as FileKeyValueStore paths are limited to 0x100 anyway. */
|
||||
using Path = typename LeastRecentlyUsedList::Path;
|
||||
private:
|
||||
FileKeyValueStore m_kvs;
|
||||
LeastRecentlyUsedList m_lru_list;
|
||||
private:
|
||||
static constexpr Path GetLeastRecentlyUsedListPath(const char *dir) {
|
||||
return Path::MakeFormat("%s/%s", dir, "lru_list.dat");
|
||||
}
|
||||
|
||||
static constexpr Path GetFileKeyValueStorePath(const char *dir) {
|
||||
return Path::MakeFormat("%s/%s", dir, "kvs");
|
||||
}
|
||||
|
||||
static Result Exists(bool *out, const char *path, fs::DirectoryEntryType type) {
|
||||
/* Set out to false initially. */
|
||||
*out = false;
|
||||
|
||||
/* Try to get the entry type. */
|
||||
fs::DirectoryEntryType entry_type;
|
||||
R_TRY_CATCH(fs::GetEntryType(std::addressof(entry_type), path)) {
|
||||
/* If the path doesn't exist, nothing has gone wrong. */
|
||||
R_CONVERT(fs::ResultPathNotFound, ResultSuccess());
|
||||
} R_END_TRY_CATCH;
|
||||
|
||||
/* Check that the entry type is correct. */
|
||||
R_UNLESS(entry_type == type, kvdb::ResultInvalidFilesystemState());
|
||||
|
||||
/* The entry exists and is the correct type. */
|
||||
*out = true;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
static Result DirectoryExists(bool *out, const char *path) {
|
||||
R_RETURN(Exists(out, path, fs::DirectoryEntryType_Directory));
|
||||
}
|
||||
|
||||
static Result FileExists(bool *out, const char *path) {
|
||||
R_RETURN(Exists(out, path, fs::DirectoryEntryType_File));
|
||||
}
|
||||
public:
|
||||
static Result CreateNewCache(const char *dir) {
|
||||
/* Make a new key value store filesystem, and a new lru_list.dat. */
|
||||
R_TRY(LeastRecentlyUsedList::CreateNewList(GetLeastRecentlyUsedListPath(dir)));
|
||||
R_TRY(fs::CreateDirectory(dir));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
static Result ValidateExistingCache(const char *dir) {
|
||||
/* Check for existence. */
|
||||
bool has_lru = false, has_kvs = false;
|
||||
R_TRY(FileExists(std::addressof(has_lru), GetLeastRecentlyUsedListPath(dir)));
|
||||
R_TRY(DirectoryExists(std::addressof(has_kvs), GetFileKeyValueStorePath(dir)));
|
||||
|
||||
/* If neither exists, CreateNewCache was never called. */
|
||||
R_UNLESS(has_lru || has_kvs, kvdb::ResultNotCreated());
|
||||
|
||||
/* If one exists but not the other, we have an invalid state. */
|
||||
R_UNLESS(has_lru && has_kvs, kvdb::ResultInvalidFilesystemState());
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
private:
|
||||
void RemoveOldestKey() {
|
||||
const Key &oldest_key = m_lru_list.Peek();
|
||||
m_lru_list.Pop();
|
||||
m_kvs.Remove(oldest_key);
|
||||
}
|
||||
public:
|
||||
Result Initialize(const char *dir, void *buf, size_t size) {
|
||||
/* Initialize list. */
|
||||
R_TRY(m_lru_list.Initialize(GetLeastRecentlyUsedListPath(dir), buf, size));
|
||||
|
||||
/* Initialize kvs. */
|
||||
/* NOTE: Despite creating the kvs folder and returning an error if it does not exist, */
|
||||
/* Nintendo does not use the kvs folder, and instead uses the passed dir. */
|
||||
/* This causes lru_list.dat to be in the same directory as the store's .val files */
|
||||
/* instead of in the same directory as a folder containing the store's .val files. */
|
||||
/* This is probably a Nintendo bug, but because system saves contain data in the wrong */
|
||||
/* layout it can't really be fixed without breaking existing devices... */
|
||||
R_TRY(m_kvs.Initialize(dir));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
size_t GetCount() const {
|
||||
return m_lru_list.GetCount();
|
||||
}
|
||||
|
||||
size_t GetCapacity() const {
|
||||
return Capacity;
|
||||
}
|
||||
|
||||
Key GetKey(size_t i) const {
|
||||
return m_lru_list.Get(i);
|
||||
}
|
||||
|
||||
bool Contains(const Key &key) const {
|
||||
return m_lru_list.Contains(key);
|
||||
}
|
||||
|
||||
Result Get(size_t *out_size, void *out_value, size_t max_out_size, const Key &key) {
|
||||
/* Note that we accessed the key. */
|
||||
m_lru_list.Update(key);
|
||||
R_RETURN(m_kvs.Get(out_size, out_value, max_out_size, key));
|
||||
}
|
||||
|
||||
template<typename Value>
|
||||
Result Get(Value *out_value, const Key &key) {
|
||||
/* Note that we accessed the key. */
|
||||
m_lru_list.Update(key);
|
||||
R_RETURN(m_kvs.Get(out_value, key));
|
||||
}
|
||||
|
||||
Result GetSize(size_t *out_size, const Key &key) {
|
||||
R_RETURN(m_kvs.GetSize(out_size, key));
|
||||
}
|
||||
|
||||
Result Set(const Key &key, const void *value, size_t value_size) {
|
||||
if (m_lru_list.Update(key)) {
|
||||
/* If an entry for the key exists, delete the existing value file. */
|
||||
m_kvs.Remove(key);
|
||||
} else {
|
||||
/* If the list is full, we need to remove the oldest key. */
|
||||
if (m_lru_list.IsFull()) {
|
||||
this->RemoveOldestKey();
|
||||
}
|
||||
|
||||
/* Add the key to the list. */
|
||||
m_lru_list.Push(key);
|
||||
}
|
||||
|
||||
/* Loop, trying to save the new value to disk. */
|
||||
while (true) {
|
||||
/* Try to set the key. */
|
||||
R_TRY_CATCH(m_kvs.Set(key, value, value_size)) {
|
||||
R_CATCH(fs::ResultNotEnoughFreeSpace) {
|
||||
/* If our entry is the only thing in the Lru list, remove it. */
|
||||
if (m_lru_list.GetCount() == 1) {
|
||||
m_lru_list.Pop();
|
||||
R_TRY(m_lru_list.Save());
|
||||
R_THROW(fs::ResultNotEnoughFreeSpace());
|
||||
}
|
||||
|
||||
/* Otherwise, remove the oldest element from the cache and try again. */
|
||||
this->RemoveOldestKey();
|
||||
continue;
|
||||
}
|
||||
} R_END_TRY_CATCH;
|
||||
|
||||
/* If we got here, we succeeded. */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Save the list. */
|
||||
R_TRY(m_lru_list.Save());
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
template<typename Value>
|
||||
Result Set(const Key &key, const Value &value) {
|
||||
R_RETURN(this->Set(key, &value, sizeof(Value)));
|
||||
}
|
||||
|
||||
Result Remove(const Key &key) {
|
||||
/* Remove the key. */
|
||||
m_lru_list.Remove(key);
|
||||
R_TRY(m_kvs.Remove(key));
|
||||
R_TRY(m_lru_list.Save());
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result RemoveAll() {
|
||||
/* TODO: Nintendo doesn't check errors here. Should we? */
|
||||
while (!m_lru_list.IsEmpty()) {
|
||||
this->RemoveOldestKey();
|
||||
}
|
||||
R_TRY(m_lru_list.Save());
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere/os.hpp>
|
||||
#include <stratosphere/fs/fs_directory.hpp>
|
||||
#include <stratosphere/kvdb/kvdb_bounded_string.hpp>
|
||||
|
||||
namespace ams::kvdb {
|
||||
|
||||
class FileKeyValueStore {
|
||||
NON_COPYABLE(FileKeyValueStore);
|
||||
NON_MOVEABLE(FileKeyValueStore);
|
||||
public:
|
||||
static constexpr size_t MaxFileLength = 0xFF;
|
||||
static constexpr char FileExtension[5] = ".val";
|
||||
static constexpr size_t FileExtensionLength = sizeof(FileExtension) - 1;
|
||||
static constexpr size_t MaxKeySize = (MaxFileLength - FileExtensionLength) / 2;
|
||||
using Path = kvdb::BoundedString<fs::EntryNameLengthMax>;
|
||||
using FileName = kvdb::BoundedString<MaxFileLength>;
|
||||
private:
|
||||
/* Subtypes. */
|
||||
struct Entry {
|
||||
u8 key[MaxKeySize];
|
||||
void *value;
|
||||
size_t key_size;
|
||||
size_t value_size;
|
||||
};
|
||||
static_assert(util::is_pod<Entry>::value, "FileKeyValueStore::Entry definition!");
|
||||
|
||||
class Cache {
|
||||
private:
|
||||
u8 *m_backing_buffer = nullptr;
|
||||
size_t m_backing_buffer_size = 0;
|
||||
size_t m_backing_buffer_free_offset = 0;
|
||||
Entry *m_entries = nullptr;
|
||||
size_t m_count = 0;
|
||||
size_t m_capacity = 0;
|
||||
private:
|
||||
void *Allocate(size_t size);
|
||||
|
||||
bool HasEntries() const {
|
||||
return m_entries != nullptr && m_capacity != 0;
|
||||
}
|
||||
public:
|
||||
Result Initialize(void *buffer, size_t buffer_size, size_t capacity);
|
||||
void Invalidate();
|
||||
util::optional<size_t> TryGet(void *out_value, size_t max_out_size, const void *key, size_t key_size);
|
||||
util::optional<size_t> TryGetSize(const void *key, size_t key_size);
|
||||
void Set(const void *key, size_t key_size, const void *value, size_t value_size);
|
||||
bool Contains(const void *key, size_t key_size);
|
||||
};
|
||||
private:
|
||||
os::SdkMutex m_lock;
|
||||
Path m_dir_path;
|
||||
Cache m_cache;
|
||||
private:
|
||||
Path GetPath(const void *key, size_t key_size);
|
||||
Result GetKey(size_t *out_size, void *out_key, size_t max_out_size, const FileName &file_name);
|
||||
public:
|
||||
FileKeyValueStore() : m_lock() { /* ... */ }
|
||||
|
||||
/* Basic accessors. */
|
||||
Result Initialize(const char *dir);
|
||||
Result InitializeWithCache(const char *dir, void *cache_buffer, size_t cache_buffer_size, size_t cache_capacity);
|
||||
Result Get(size_t *out_size, void *out_value, size_t max_out_size, const void *key, size_t key_size);
|
||||
Result GetSize(size_t *out_size, const void *key, size_t key_size);
|
||||
Result Set(const void *key, size_t key_size, const void *value, size_t value_size);
|
||||
Result Remove(const void *key, size_t key_size);
|
||||
|
||||
/* Niceties. */
|
||||
template<typename Key>
|
||||
Result Get(size_t *out_size, void *out_value, size_t max_out_size, const Key &key) {
|
||||
static_assert(util::is_pod<Key>::value && sizeof(Key) <= MaxKeySize, "Invalid FileKeyValueStore Key!");
|
||||
R_RETURN(this->Get(out_size, out_value, max_out_size, std::addressof(key), sizeof(Key)));
|
||||
}
|
||||
|
||||
template<typename Key, typename Value>
|
||||
Result Get(Value *out_value, const Key &key) {
|
||||
static_assert(util::is_pod<Value>::value && !std::is_pointer<Value>::value, "Invalid FileKeyValueStore Value!");
|
||||
size_t size = 0;
|
||||
R_TRY(this->Get(std::addressof(size), out_value, sizeof(Value), key));
|
||||
AMS_ABORT_UNLESS(size >= sizeof(Value));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
template<typename Key>
|
||||
Result GetSize(size_t *out_size, const Key &key) {
|
||||
R_RETURN(this->GetSize(out_size, std::addressof(key), sizeof(Key)));
|
||||
}
|
||||
|
||||
template<typename Key>
|
||||
Result Set(const Key &key, const void *value, size_t value_size) {
|
||||
static_assert(util::is_pod<Key>::value && sizeof(Key) <= MaxKeySize, "Invalid FileKeyValueStore Key!");
|
||||
R_RETURN(this->Set(std::addressof(key), sizeof(Key), value, value_size));
|
||||
}
|
||||
|
||||
template<typename Key, typename Value>
|
||||
Result Set(const Key &key, const Value &value) {
|
||||
static_assert(util::is_pod<Value>::value && !std::is_pointer<Value>::value, "Invalid FileKeyValueStore Value!");
|
||||
R_RETURN(this->Set(key, std::addressof(value), sizeof(Value)));
|
||||
}
|
||||
|
||||
template<typename Key>
|
||||
Result Remove(const Key &key) {
|
||||
R_RETURN(this->Remove(std::addressof(key), sizeof(Key)));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,554 +0,0 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <stratosphere/fs/fs_file.hpp>
|
||||
#include <stratosphere/fs/fs_directory.hpp>
|
||||
#include <stratosphere/fs/fs_filesystem.hpp>
|
||||
#include <stratosphere/kvdb/kvdb_auto_buffer.hpp>
|
||||
#include <stratosphere/kvdb/kvdb_archive.hpp>
|
||||
#include <stratosphere/kvdb/kvdb_bounded_string.hpp>
|
||||
|
||||
namespace ams::kvdb {
|
||||
|
||||
template<class Key>
|
||||
class MemoryKeyValueStore {
|
||||
static_assert(util::is_pod<Key>::value, "KeyValueStore Keys must be pod!");
|
||||
NON_COPYABLE(MemoryKeyValueStore);
|
||||
NON_MOVEABLE(MemoryKeyValueStore);
|
||||
public:
|
||||
/* Subtypes. */
|
||||
class Entry {
|
||||
private:
|
||||
Key m_key;
|
||||
void *m_value;
|
||||
size_t m_value_size;
|
||||
public:
|
||||
constexpr Entry(const Key &k, void *v, size_t s) : m_key(k), m_value(v), m_value_size(s) { /* ... */ }
|
||||
|
||||
const Key &GetKey() const {
|
||||
return m_key;
|
||||
}
|
||||
|
||||
template<typename Value = void>
|
||||
Value *GetValuePointer() {
|
||||
/* Size check. Note: Nintendo does not size check. */
|
||||
if constexpr (!std::is_same<Value, void>::value) {
|
||||
AMS_ABORT_UNLESS(sizeof(Value) <= m_value_size);
|
||||
/* Ensure we only get pod. */
|
||||
static_assert(util::is_pod<Value>::value, "KeyValueStore Values must be pod");
|
||||
}
|
||||
return reinterpret_cast<Value *>(m_value);
|
||||
}
|
||||
|
||||
template<typename Value = void>
|
||||
const Value *GetValuePointer() const {
|
||||
/* Size check. Note: Nintendo does not size check. */
|
||||
if constexpr (!std::is_same<Value, void>::value) {
|
||||
AMS_ABORT_UNLESS(sizeof(Value) <= m_value_size);
|
||||
/* Ensure we only get pod. */
|
||||
static_assert(util::is_pod<Value>::value, "KeyValueStore Values must be pod");
|
||||
}
|
||||
return reinterpret_cast<Value *>(m_value);
|
||||
}
|
||||
|
||||
template<typename Value>
|
||||
Value &GetValue() {
|
||||
return *(this->GetValuePointer<Value>());
|
||||
}
|
||||
|
||||
template<typename Value>
|
||||
const Value &GetValue() const {
|
||||
return *(this->GetValuePointer<Value>());
|
||||
}
|
||||
|
||||
size_t GetValueSize() const {
|
||||
return m_value_size;
|
||||
}
|
||||
|
||||
constexpr inline bool operator<(const Key &rhs) const {
|
||||
return m_key < rhs;
|
||||
}
|
||||
|
||||
constexpr inline bool operator==(const Key &rhs) const {
|
||||
return m_key == rhs;
|
||||
}
|
||||
};
|
||||
|
||||
class Index {
|
||||
private:
|
||||
size_t m_count;
|
||||
size_t m_capacity;
|
||||
Entry *m_entries;
|
||||
MemoryResource *m_memory_resource;
|
||||
public:
|
||||
Index() : m_count(0), m_capacity(0), m_entries(nullptr), m_memory_resource(nullptr) { /* ... */ }
|
||||
|
||||
~Index() {
|
||||
if (m_entries != nullptr) {
|
||||
this->ResetEntries();
|
||||
m_memory_resource->Deallocate(m_entries, sizeof(Entry) * m_capacity);
|
||||
m_entries = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
size_t GetCount() const {
|
||||
return m_count;
|
||||
}
|
||||
|
||||
size_t GetCapacity() const {
|
||||
return m_capacity;
|
||||
}
|
||||
|
||||
void ResetEntries() {
|
||||
for (size_t i = 0; i < m_count; i++) {
|
||||
m_memory_resource->Deallocate(m_entries[i].GetValuePointer(), m_entries[i].GetValueSize());
|
||||
}
|
||||
m_count = 0;
|
||||
}
|
||||
|
||||
Result Initialize(size_t capacity, MemoryResource *mr) {
|
||||
m_entries = reinterpret_cast<Entry *>(mr->Allocate(sizeof(Entry) * capacity));
|
||||
R_UNLESS(m_entries != nullptr, kvdb::ResultAllocationFailed());
|
||||
m_capacity = capacity;
|
||||
m_memory_resource = mr;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result Set(const Key &key, const void *value, size_t value_size) {
|
||||
/* Find entry for key. */
|
||||
Entry *it = this->lower_bound(key);
|
||||
if (it != this->end() && it->GetKey() == key) {
|
||||
/* Entry already exists. Free old value. */
|
||||
m_memory_resource->Deallocate(it->GetValuePointer(), it->GetValueSize());
|
||||
} else {
|
||||
/* We need to add a new entry. Check we have room, move future keys forward. */
|
||||
R_UNLESS(m_count < m_capacity, kvdb::ResultOutOfKeyResource());
|
||||
std::memmove(it + 1, it, sizeof(*it) * (this->end() - it));
|
||||
m_count++;
|
||||
}
|
||||
|
||||
/* Allocate new value. */
|
||||
void *new_value = m_memory_resource->Allocate(value_size);
|
||||
R_UNLESS(new_value != nullptr, kvdb::ResultAllocationFailed());
|
||||
std::memcpy(new_value, value, value_size);
|
||||
|
||||
/* Save the new Entry in the map. */
|
||||
*it = Entry(key, new_value, value_size);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result AddUnsafe(const Key &key, void *value, size_t value_size) {
|
||||
R_UNLESS(m_count < m_capacity, kvdb::ResultOutOfKeyResource());
|
||||
|
||||
m_entries[m_count++] = Entry(key, value, value_size);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result Remove(const Key &key) {
|
||||
/* Find entry for key. */
|
||||
Entry *it = this->find(key);
|
||||
R_UNLESS(it != this->end(), kvdb::ResultKeyNotFound());
|
||||
|
||||
/* Free the value, move entries back. */
|
||||
m_memory_resource->Deallocate(it->GetValuePointer(), it->GetValueSize());
|
||||
std::memmove(it, it + 1, sizeof(*it) * (this->end() - (it + 1)));
|
||||
m_count--;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Entry *begin() {
|
||||
return this->GetBegin();
|
||||
}
|
||||
|
||||
const Entry *begin() const {
|
||||
return this->GetBegin();
|
||||
}
|
||||
|
||||
Entry *end() {
|
||||
return this->GetEnd();
|
||||
}
|
||||
|
||||
const Entry *end() const {
|
||||
return this->GetEnd();
|
||||
}
|
||||
|
||||
const Entry *cbegin() const {
|
||||
return this->begin();
|
||||
}
|
||||
|
||||
const Entry *cend() const {
|
||||
return this->end();
|
||||
}
|
||||
|
||||
Entry *lower_bound(const Key &key) {
|
||||
return this->GetLowerBound(key);
|
||||
}
|
||||
|
||||
const Entry *lower_bound(const Key &key) const {
|
||||
return this->GetLowerBound(key);
|
||||
}
|
||||
|
||||
Entry *find(const Key &key) {
|
||||
return this->Find(key);
|
||||
}
|
||||
|
||||
const Entry *find(const Key &key) const {
|
||||
return this->Find(key);
|
||||
}
|
||||
private:
|
||||
Entry *GetBegin() {
|
||||
return m_entries;
|
||||
}
|
||||
|
||||
const Entry *GetBegin() const {
|
||||
return m_entries;
|
||||
}
|
||||
|
||||
Entry *GetEnd() {
|
||||
return this->GetBegin() + m_count;
|
||||
}
|
||||
|
||||
const Entry *GetEnd() const {
|
||||
return this->GetBegin() + m_count;
|
||||
}
|
||||
|
||||
Entry *GetLowerBound(const Key &key) {
|
||||
return std::lower_bound(this->GetBegin(), this->GetEnd(), key);
|
||||
}
|
||||
|
||||
const Entry *GetLowerBound(const Key &key) const {
|
||||
return std::lower_bound(this->GetBegin(), this->GetEnd(), key);
|
||||
}
|
||||
|
||||
Entry *Find(const Key &key) {
|
||||
auto it = this->GetLowerBound(key);
|
||||
auto end = this->GetEnd();
|
||||
if (it != end && it->GetKey() == key) {
|
||||
return it;
|
||||
}
|
||||
return end;
|
||||
}
|
||||
|
||||
const Entry *Find(const Key &key) const {
|
||||
auto it = this->GetLowerBound(key);
|
||||
auto end = this->GetEnd();
|
||||
if (it != end && it->GetKey() == key) {
|
||||
return it;
|
||||
}
|
||||
return end;
|
||||
}
|
||||
};
|
||||
private:
|
||||
using Path = kvdb::BoundedString<fs::EntryNameLengthMax>;
|
||||
private:
|
||||
Index m_index;
|
||||
Path m_path;
|
||||
Path m_temp_path;
|
||||
MemoryResource *m_memory_resource;
|
||||
public:
|
||||
MemoryKeyValueStore() { /* ... */ }
|
||||
|
||||
Result Initialize(const char *dir, size_t capacity, MemoryResource *mr) {
|
||||
/* Ensure that the passed path is a directory. */
|
||||
fs::DirectoryEntryType entry_type;
|
||||
R_TRY(fs::GetEntryType(std::addressof(entry_type), dir));
|
||||
R_UNLESS(entry_type == fs::DirectoryEntryType_Directory, fs::ResultPathNotFound());
|
||||
|
||||
/* Set paths. */
|
||||
m_path.AssignFormat("%s%s", dir, "/imkvdb.arc");
|
||||
m_temp_path.AssignFormat("%s%s", dir, "/imkvdb.tmp");
|
||||
|
||||
/* Initialize our index. */
|
||||
R_TRY(m_index.Initialize(capacity, mr));
|
||||
m_memory_resource = mr;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result InitializeForReadOnlyArchiveFile(const char *path, size_t capacity, MemoryResource *mr) {
|
||||
/* Ensure that the passed path is a directory. */
|
||||
fs::DirectoryEntryType entry_type;
|
||||
R_TRY(fs::GetEntryType(std::addressof(entry_type), path));
|
||||
R_UNLESS(entry_type == fs::DirectoryEntryType_File, fs::ResultPathNotFound());
|
||||
|
||||
/* Set paths. */
|
||||
m_path.Assign(path);
|
||||
m_temp_path.Assign("");
|
||||
|
||||
/* Initialize our index. */
|
||||
R_TRY(m_index.Initialize(capacity, mr));
|
||||
m_memory_resource = mr;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result Initialize(size_t capacity, MemoryResource *mr) {
|
||||
/* This initializes without an archive file. */
|
||||
/* A store initialized this way cannot have its contents loaded from or flushed to disk. */
|
||||
m_path.Assign("");
|
||||
m_temp_path.Assign("");
|
||||
|
||||
/* Initialize our index. */
|
||||
R_TRY(m_index.Initialize(capacity, mr));
|
||||
m_memory_resource = mr;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
size_t GetCount() const {
|
||||
return m_index.GetCount();
|
||||
}
|
||||
|
||||
size_t GetCapacity() const {
|
||||
return m_index.GetCapacity();
|
||||
}
|
||||
|
||||
Result Load() {
|
||||
/* Reset any existing entries. */
|
||||
m_index.ResetEntries();
|
||||
|
||||
/* Try to read the archive -- note, path not found is a success condition. */
|
||||
/* This is because no archive file = no entries, so we're in the right state. */
|
||||
AutoBuffer buffer;
|
||||
R_TRY_CATCH(this->ReadArchiveFile(std::addressof(buffer))) {
|
||||
R_CONVERT(fs::ResultPathNotFound, ResultSuccess());
|
||||
} R_END_TRY_CATCH;
|
||||
|
||||
/* Parse entries from the buffer. */
|
||||
{
|
||||
ArchiveReader reader(buffer);
|
||||
|
||||
size_t entry_count = 0;
|
||||
R_TRY(reader.ReadEntryCount(std::addressof(entry_count)));
|
||||
|
||||
for (size_t i = 0; i < entry_count; i++) {
|
||||
/* Get size of key/value. */
|
||||
size_t key_size = 0, value_size = 0;
|
||||
R_TRY(reader.GetEntrySize(std::addressof(key_size), std::addressof(value_size)));
|
||||
|
||||
/* Allocate memory for value. */
|
||||
void *new_value = m_memory_resource->Allocate(value_size);
|
||||
R_UNLESS(new_value != nullptr, kvdb::ResultAllocationFailed());
|
||||
|
||||
/* If we fail before adding to the index, deallocate our value. */
|
||||
ON_RESULT_FAILURE { m_memory_resource->Deallocate(new_value, value_size); };
|
||||
|
||||
/* Read key and value. */
|
||||
Key key;
|
||||
R_TRY(reader.ReadEntry(std::addressof(key), sizeof(key), new_value, value_size));
|
||||
R_TRY(m_index.AddUnsafe(key, new_value, value_size));
|
||||
}
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result Save(bool destructive = false) {
|
||||
/* Create a buffer to hold the archive. */
|
||||
AutoBuffer buffer;
|
||||
R_TRY(buffer.Initialize(this->GetArchiveSize()));
|
||||
|
||||
/* Write the archive to the buffer. */
|
||||
{
|
||||
ArchiveWriter writer(buffer);
|
||||
writer.WriteHeader(this->GetCount());
|
||||
for (const auto &it : m_index) {
|
||||
const auto &key = it.GetKey();
|
||||
writer.WriteEntry(std::addressof(key), sizeof(Key), it.GetValuePointer(), it.GetValueSize());
|
||||
}
|
||||
}
|
||||
|
||||
/* Save the buffer to disk. */
|
||||
R_RETURN(this->Commit(buffer, destructive));
|
||||
}
|
||||
|
||||
Result Set(const Key &key, const void *value, size_t value_size) {
|
||||
R_RETURN(m_index.Set(key, value, value_size));
|
||||
}
|
||||
|
||||
template<typename Value>
|
||||
Result Set(const Key &key, const Value &value) {
|
||||
/* Only allow setting pod. */
|
||||
static_assert(util::is_pod<Value>::value, "KeyValueStore Values must be pod");
|
||||
R_RETURN(this->Set(key, std::addressof(value), sizeof(Value)));
|
||||
}
|
||||
|
||||
template<typename Value>
|
||||
Result Set(const Key &key, const Value *value) {
|
||||
/* Only allow setting pod. */
|
||||
static_assert(util::is_pod<Value>::value, "KeyValueStore Values must be pod");
|
||||
R_RETURN(this->Set(key, value, sizeof(Value)));
|
||||
}
|
||||
|
||||
Result Get(size_t *out_size, void *out_value, size_t max_out_size, const Key &key) {
|
||||
/* Find entry. */
|
||||
auto it = this->find(key);
|
||||
R_UNLESS(it != this->end(), kvdb::ResultKeyNotFound());
|
||||
|
||||
size_t size = std::min(max_out_size, it->GetValueSize());
|
||||
std::memcpy(out_value, it->GetValuePointer(), size);
|
||||
*out_size = size;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
template<typename Value = void>
|
||||
Result GetValuePointer(Value **out_value, const Key &key) {
|
||||
/* Find entry. */
|
||||
auto it = this->find(key);
|
||||
R_UNLESS(it != this->end(), kvdb::ResultKeyNotFound());
|
||||
|
||||
*out_value = it->template GetValuePointer<Value>();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
template<typename Value = void>
|
||||
Result GetValuePointer(const Value **out_value, const Key &key) const {
|
||||
/* Find entry. */
|
||||
auto it = this->find(key);
|
||||
R_UNLESS(it != this->end(), kvdb::ResultKeyNotFound());
|
||||
|
||||
*out_value = it->template GetValuePointer<Value>();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
template<typename Value>
|
||||
Result GetValue(Value *out_value, const Key &key) const {
|
||||
/* Find entry. */
|
||||
auto it = this->find(key);
|
||||
R_UNLESS(it != this->end(), kvdb::ResultKeyNotFound());
|
||||
|
||||
*out_value = it->template GetValue<Value>();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetValueSize(size_t *out_size, const Key &key) const {
|
||||
/* Find entry. */
|
||||
auto it = this->find(key);
|
||||
R_UNLESS(it != this->end(), kvdb::ResultKeyNotFound());
|
||||
|
||||
*out_size = it->GetValueSize();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result Remove(const Key &key) {
|
||||
R_RETURN(m_index.Remove(key));
|
||||
}
|
||||
|
||||
Entry *begin() {
|
||||
return m_index.begin();
|
||||
}
|
||||
|
||||
const Entry *begin() const {
|
||||
return m_index.begin();
|
||||
}
|
||||
|
||||
Entry *end() {
|
||||
return m_index.end();
|
||||
}
|
||||
|
||||
const Entry *end() const {
|
||||
return m_index.end();
|
||||
}
|
||||
|
||||
const Entry *cbegin() const {
|
||||
return m_index.cbegin();
|
||||
}
|
||||
|
||||
const Entry *cend() const {
|
||||
return m_index.cend();
|
||||
}
|
||||
|
||||
Entry *lower_bound(const Key &key) {
|
||||
return m_index.lower_bound(key);
|
||||
}
|
||||
|
||||
const Entry *lower_bound(const Key &key) const {
|
||||
return m_index.lower_bound(key);
|
||||
}
|
||||
|
||||
Entry *find(const Key &key) {
|
||||
return m_index.find(key);
|
||||
}
|
||||
|
||||
const Entry *find(const Key &key) const {
|
||||
return m_index.find(key);
|
||||
}
|
||||
private:
|
||||
Result SaveArchiveToFile(const char *path, const void *buf, size_t size) {
|
||||
/* Try to delete the archive, but allow deletion failure. */
|
||||
fs::DeleteFile(path);
|
||||
|
||||
/* Create new archive. */
|
||||
R_TRY(fs::CreateFile(path, size));
|
||||
|
||||
/* Write data to the archive. */
|
||||
{
|
||||
fs::FileHandle file;
|
||||
R_TRY(fs::OpenFile(std::addressof(file), path, fs::OpenMode_Write));
|
||||
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||
R_TRY(fs::WriteFile(file, 0, buf, size, fs::WriteOption::Flush));
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result Commit(const AutoBuffer &buffer, bool destructive) {
|
||||
if (destructive) {
|
||||
/* Delete and save to the real archive. */
|
||||
R_TRY(SaveArchiveToFile(m_path.Get(), buffer.Get(), buffer.GetSize()));
|
||||
} else {
|
||||
/* Delete and save to a temporary archive. */
|
||||
R_TRY(SaveArchiveToFile(m_temp_path.Get(), buffer.Get(), buffer.GetSize()));
|
||||
|
||||
/* Try to delete the saved archive, but allow deletion failure. */
|
||||
fs::DeleteFile(m_path.Get());
|
||||
|
||||
/* Rename the path. */
|
||||
R_TRY(fs::RenameFile(m_temp_path.Get(), m_path.Get()));
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
size_t GetArchiveSize() const {
|
||||
ArchiveSizeHelper size_helper;
|
||||
|
||||
for (const auto &it : m_index) {
|
||||
size_helper.AddEntry(sizeof(Key), it.GetValueSize());
|
||||
}
|
||||
|
||||
return size_helper.GetSize();
|
||||
}
|
||||
|
||||
Result ReadArchiveFile(AutoBuffer *dst) const {
|
||||
/* Open the file. */
|
||||
fs::FileHandle file;
|
||||
R_TRY(fs::OpenFile(std::addressof(file), m_path, fs::OpenMode_Read));
|
||||
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||
|
||||
/* Get the archive file size. */
|
||||
s64 archive_size;
|
||||
R_TRY(fs::GetFileSize(std::addressof(archive_size), file));
|
||||
|
||||
/* Make a new buffer, read the file. */
|
||||
R_TRY(dst->Initialize(static_cast<size_t>(archive_size)));
|
||||
R_TRY(fs::ReadFile(file, 0, dst->Get(), dst->GetSize()));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user