diff --git a/include/BufferQueue.hpp b/include/BufferQueue.hpp index 2ff15de..4de4834 100644 --- a/include/BufferQueue.hpp +++ b/include/BufferQueue.hpp @@ -22,52 +22,30 @@ class BufferQueue final /// @brief Creates a new BufferQueue. /// @param bufferLimit Number of buffers before the queue is considered full. - BufferQueue(int bufferLimit, size_t bufferSize) noexcept - : m_bufferLimit(bufferLimit) - , m_bufferSize(bufferSize) {}; + BufferQueue(int bufferLimit) noexcept + : m_bufferLimit(bufferLimit) {}; - /// @brief Gets an returns a scoped lock_guard for the internal mutex. - inline BufferGuard lock_queue() noexcept { return BufferGuard{m_queueMutex}; } - - /// @brief Returns if the buffer queue is "full", or, has reached its limit. - inline bool is_full() const noexcept + inline bool try_push(Buffer &buffer, size_t bufferSize) { - if (static_cast(m_queue.size()) < m_bufferLimit) { return false; } + std::lock_guard queueGuard{m_queueMutex}; + if (m_bufferLimit != NO_LIMIT && static_cast(m_queue.size()) >= m_bufferLimit) { return false; } - std::this_thread::sleep_for(std::chrono::microseconds(10)); + m_queue.emplace(std::move(buffer), bufferSize); return true; } - /// @brief Returns if the buffer queue is empty. - inline bool is_empty() const noexcept + inline bool get_front(QueuePair &pairOut) { - if (!m_queue.empty()) { return false; } + std::lock_guard queueGuard{m_queueMutex}; + if (m_queue.empty()) { return false; } - std::this_thread::sleep_for(std::chrono::microseconds(10)); - return true; - } - - /// @brief Creates and returns a buffer of bufferSize. - inline Buffer allocate_buffer() { return std::make_unique(m_bufferSize); } - - /// @brief Same as above, but with the size passed here. - inline Buffer allocate_buffer(size_t bufferSize) { return std::make_unique(bufferSize); } - - /// @brief Pushes a new buffer to the queue. - inline void push_to_queue(Buffer &buffer, size_t bufferSize) - { - auto queuePair = std::make_pair(std::move(buffer), bufferSize); - m_queue.push(std::move(queuePair)); - } - - /// @brief Returns the front of the queue and pops it. - inline QueuePair get_front() - { - auto front = std::move(m_queue.front()); + pairOut = std::move(m_queue.front()); m_queue.pop(); - return front; + return true; } + static inline void default_delay() noexcept { std::this_thread::sleep_for(std::chrono::microseconds(10)); } + /// @brief This can be passed to the constructor to make is_full() return false all of the time. static inline int NO_LIMIT = -1; diff --git a/include/curl/DownloadStruct.hpp b/include/curl/DownloadStruct.hpp index 93cd5c2..b1fc692 100644 --- a/include/curl/DownloadStruct.hpp +++ b/include/curl/DownloadStruct.hpp @@ -10,12 +10,13 @@ namespace curl { + inline constexpr int DOWNLOAD_QUEUE_LIMIT = 128; // clang-format off struct DownloadStruct : sys::threadpool::DataStruct { /// @brief Buffer queue for downloads. - BufferQueue bufferQueue{BufferQueue::NO_LIMIT, 0}; + BufferQueue bufferQueue{BufferQueue::NO_LIMIT}; /// @brief Destination file to write to. fslib::File *dest{}; diff --git a/source/curl/curl.cpp b/source/curl/curl.cpp index 0750831..0bca68c 100644 --- a/source/curl/curl.cpp +++ b/source/curl/curl.cpp @@ -71,13 +71,10 @@ size_t curl::download_file_threaded(const char *buffer, size_t size, size_t coun auto &bufferQueue = download->bufferQueue; const size_t downloadSize = size * count; - auto chunkBuffer = bufferQueue.allocate_buffer(downloadSize); + auto chunkBuffer = std::make_unique(downloadSize); std::copy(buffer, buffer + downloadSize, chunkBuffer.get()); - { - auto queueGuard = bufferQueue.lock_queue(); - bufferQueue.push_to_queue(chunkBuffer, downloadSize); - } + while (!bufferQueue.try_push(chunkBuffer, downloadSize)) { std::this_thread::sleep_for(std::chrono::microseconds(10)); } return downloadSize; } @@ -95,17 +92,10 @@ void curl::download_write_thread_function(sys::threadpool::JobData jobData) for (int64_t i = 0; i < fileSize;) { BufferQueue::QueuePair queuePair{}; - { - auto queueGuard = bufferQueue.lock_queue(); - if (bufferQueue.is_empty()) { continue; } - - queuePair = bufferQueue.get_front(); - } + while (!bufferQueue.get_front(queuePair)) { BufferQueue::default_delay(); } auto &[chunkBuffer, bufferSize] = queuePair; - dest.write(chunkBuffer.get(), bufferSize); - i += bufferSize; if (task) { task->update_current(static_cast(i)); } diff --git a/source/fs/io.cpp b/source/fs/io.cpp index 1f8ea7b..d322e71 100644 --- a/source/fs/io.cpp +++ b/source/fs/io.cpp @@ -25,7 +25,7 @@ namespace // clang-format off struct FileThreadStruct : sys::threadpool::DataStruct { - BufferQueue bufferQueue{SIZE_QUEUE_LIMIT, SIZE_FILE_BUFFER}; + BufferQueue bufferQueue{SIZE_QUEUE_LIMIT}; fslib::File *source{}; }; // clang-format on @@ -41,16 +41,10 @@ static void read_thread_function(sys::threadpool::JobData jobData) for (int64_t i = 0; i < fileSize;) { - ssize_t readSize{}; - { - auto queueGuard = bufferQueue.lock_queue(); - if (bufferQueue.is_full()) { continue; } - - auto readBuffer = bufferQueue.allocate_buffer(); - readSize = source.read(readBuffer.get(), SIZE_FILE_BUFFER); - bufferQueue.push_to_queue(readBuffer, readSize); - } + auto chunkBuffer = std::make_unique(SIZE_FILE_BUFFER); + ssize_t readSize = source.read(chunkBuffer.get(), SIZE_FILE_BUFFER); + while (!bufferQueue.try_push(chunkBuffer, readSize)) { BufferQueue::default_delay(); } i += readSize; } } @@ -80,12 +74,7 @@ void fs::copy_file(const fslib::Path &source, const fslib::Path &destination, sy for (int64_t i = 0; i < sourceSize;) { BufferQueue::QueuePair queuePair{}; - { - auto queueGuard = bufferQueue.lock_queue(); - if (bufferQueue.is_empty()) { continue; } - - queuePair = bufferQueue.get_front(); - } + while (!bufferQueue.get_front(queuePair)) { BufferQueue::default_delay(); } auto &[buffer, bufferSize] = queuePair; destFile.write(buffer.get(), bufferSize); @@ -127,12 +116,7 @@ void fs::copy_file_commit(const fslib::Path &source, for (int64_t i = 0; i < sourceSize;) { BufferQueue::QueuePair queuePair{}; - { - auto queueGuard = bufferQueue.lock_queue(); - if (bufferQueue.is_empty()) { continue; } - - queuePair = bufferQueue.get_front(); - } + while (!bufferQueue.get_front(queuePair)) { BufferQueue::default_delay(); } const auto &[buffer, bufferSize] = queuePair; const bool needsCommit = journalCount + static_cast(bufferSize) >= journalSize; diff --git a/source/fs/zip.cpp b/source/fs/zip.cpp index c15a7b8..5eca8de 100644 --- a/source/fs/zip.cpp +++ b/source/fs/zip.cpp @@ -29,13 +29,13 @@ namespace struct ZipReadStruct : sys::threadpool::DataStruct { fslib::File *source{}; - BufferQueue bufferQueue{SIZE_BUFFER_LIMIT, SIZE_ZIP_BUFFER}; + BufferQueue bufferQueue{SIZE_BUFFER_LIMIT}; }; struct UnzipReadStruct : sys::threadpool::DataStruct { fs::MiniUnzip *unzip{}; - BufferQueue bufferQueue{SIZE_BUFFER_LIMIT, SIZE_UNZIP_BUFFER}; + BufferQueue bufferQueue{SIZE_BUFFER_LIMIT}; }; // clang-format on } // namespace @@ -51,16 +51,10 @@ static void zip_read_thread_function(sys::threadpool::JobData jobData) for (int64_t i = 0; i < fileSize;) { - ssize_t readSize{}; - { - auto queueGuard = bufferQueue.lock_queue(); - if (bufferQueue.is_full()) { continue; } - - auto readBuffer = bufferQueue.allocate_buffer(); - readSize = source.read(readBuffer.get(), SIZE_ZIP_BUFFER); - bufferQueue.push_to_queue(readBuffer, readSize); - } + auto chunkBuffer = std::make_unique(SIZE_ZIP_BUFFER); + ssize_t readSize = source.read(chunkBuffer.get(), SIZE_ZIP_BUFFER); + while (!bufferQueue.try_push(chunkBuffer, readSize)) { BufferQueue::default_delay(); } i += readSize; } } @@ -76,16 +70,10 @@ static void unzip_read_thread_function(sys::threadpool::JobData jobData) for (int64_t i = 0; i < fileSize;) { - ssize_t readSize{}; - { - auto queueGuard = bufferQueue.lock_queue(); - if (bufferQueue.is_full()) { continue; } - - auto readBuffer = bufferQueue.allocate_buffer(); - readSize = unzip.read(readBuffer.get(), SIZE_UNZIP_BUFFER); - bufferQueue.push_to_queue(readBuffer, readSize); - } + auto chunkBuffer = std::make_unique(SIZE_UNZIP_BUFFER); + ssize_t readSize = unzip.read(chunkBuffer.get(), SIZE_UNZIP_BUFFER); + while (!bufferQueue.try_push(chunkBuffer, readSize)) { BufferQueue::default_delay(); } i += readSize; } } @@ -128,12 +116,7 @@ void fs::copy_directory_to_zip(const fslib::Path &source, fs::MiniZip &dest, sys for (int64_t i = 0; i < fileSize;) { BufferQueue::QueuePair queuePair{}; - { - auto queueGuard = bufferQueue.lock_queue(); - if (bufferQueue.is_empty()) { continue; } - - queuePair = bufferQueue.get_front(); - } + while (!bufferQueue.get_front(queuePair)) { BufferQueue::default_delay(); } auto &[buffer, bufferSize] = queuePair; dest.write(buffer.get(), bufferSize); @@ -199,16 +182,10 @@ void fs::copy_zip_to_directory(fs::MiniUnzip &unzip, const fslib::Path &dest, in for (int64_t i = 0; i < fileSize;) { BufferQueue::QueuePair queuePair{}; - { - auto queueGuard = bufferQueue.lock_queue(); - if (bufferQueue.is_empty()) { continue; } - - queuePair = bufferQueue.get_front(); - } + while (!bufferQueue.get_front(queuePair)) { BufferQueue::default_delay(); } auto &[buffer, bufferSize] = queuePair; - - const bool commitNeeded = needCommits && journalCount + static_cast(bufferSize) >= journalSize; + const bool commitNeeded = needCommits && journalCount + static_cast(bufferSize) >= journalSize; if (commitNeeded) { destFile.close(); diff --git a/source/stringutil.cpp b/source/stringutil.cpp index 6261834..94dfca9 100644 --- a/source/stringutil.cpp +++ b/source/stringutil.cpp @@ -7,7 +7,6 @@ #include #include #include -#include namespace { @@ -19,8 +18,7 @@ namespace {L',', L'/', L'\\', L'<', L'>', L':', L'"', L'|', L'?', L'*', L'™', L'©', L'®'}; /// @brief This is a table for replacing accented characters and "look alike" unicode characters. - // Note: extra outer braces required to initialize std::array of non-scalar elements (pairs) - constexpr std::array, 78> REPLACEMENT_TABLE{ + constexpr std::array, 78> REPLACEMENT_TABLE = { {{L'Á', "A"}, {L'À', "A"}, {L'Â', "A"}, {L'Ä', "A"}, {L'Ã', "A"}, {L'Å', "A"}, {L'á', "a"}, {L'à', "a"}, {L'â', "a"}, {L'ä', "a"}, {L'ã', "a"}, {L'å', "a"}, {L'É', "E"}, {L'È', "E"}, {L'Ê', "E"}, {L'Ë', "E"}, {L'é', "e"}, {L'è', "e"}, {L'ê', "e"}, {L'ë', "e"}, {L'Í', "I"}, {L'Ì', "I"}, {L'Î', "I"}, {L'Ï', "I"}, @@ -88,7 +86,7 @@ bool stringutil::sanitize_string_for_path(const char *stringIn, char *stringOut, // Check for replacing. const auto &replace = std::find_if(REPLACEMENT_TABLE.begin(), REPLACEMENT_TABLE.end(), - [=](const auto &replacePair) { return replacePair.first == codepoint; }); + [codepoint](const auto &replacePair) { return replacePair.first == codepoint; }); if (replace != REPLACEMENT_TABLE.end()) { const auto &[tablePoint, replacement] = *replace; diff --git a/source/tasks/saveimport.cpp b/source/tasks/saveimport.cpp index 86e4c79..245d17d 100644 --- a/source/tasks/saveimport.cpp +++ b/source/tasks/saveimport.cpp @@ -5,6 +5,7 @@ #include "error.hpp" #include "fs/fs.hpp" #include "fslib.hpp" +#include "logging/logger.hpp" #include "strings/strings.hpp" #include "stringutil.hpp" #include "ui/PopMessageManager.hpp" @@ -12,35 +13,24 @@ // Defined at bottom. static bool read_save_meta(const fslib::Path &path, fs::SaveMetaData &metaOut); static bool test_for_save(data::User *user, const fs::SaveMetaData &saveMeta); +static bool create_save_data_from_meta(sys::ProgressTask *task, data::User *user, const fs::SaveMetaData &saveMeta); void tasks::saveimport::import_save_backup(sys::threadpool::JobData taskData) { auto castData = std::static_pointer_cast(taskData); - sys::ProgressTask *task = static_cast(castData->task); - if (error::is_null(task)) { return; } - + sys::ProgressTask *task = static_cast(castData->task); data::User *user = castData->user; const fslib::Path &target = castData->path; + if (error::is_null(task) || error::is_null(user)) { return; } fs::SaveMetaData metaData{}; const bool metaRead = read_save_meta(target, metaData); if (!metaRead || metaData.revision < 1) { TASK_FINISH_RETURN(task); } - const bool saveExists = test_for_save(user, metaData); - if (!saveExists) - { - // Gonna borrow this. - const char *statusFormat = strings::get_by_name(strings::names::USEROPTION_STATUS, 0); - const std::string hexID = stringutil::get_formatted_string("%016llX", metaData.applicationID); - std::string status = stringutil::get_formatted_string(statusFormat, hexID.c_str()); - task->set_status(status); - - fs::create_save_data_for(user, metaData); - user->clear_data_entries(); - user->load_user_data(); - MainMenuState::refresh_view_states(); - } + const bool saveExists = test_for_save(user, metaData); + const bool saveCreated = !saveExists && create_save_data_from_meta(task, user, metaData); + if (!saveExists && !saveCreated) { TASK_FINISH_RETURN(task); } const FsSaveDataInfo *saveInfo = user->get_save_info_by_id(metaData.applicationID); if (error::is_null(saveInfo)) { TASK_FINISH_RETURN(task); } @@ -135,3 +125,30 @@ static bool test_for_save(data::User *user, const fs::SaveMetaData &saveMeta) return mounted; } + +static bool create_save_data_from_meta(sys::ProgressTask *task, data::User *user, const fs::SaveMetaData &saveMeta) +{ + if (error::is_null(task) || error::is_null(user)) { return false; } + + // Gonna borrow this. + const char *statusFormat = strings::get_by_name(strings::names::USEROPTION_STATUS, 0); + const std::string hexID = stringutil::get_formatted_string("%016llX", saveMeta.applicationID); + std::string status = stringutil::get_formatted_string(statusFormat, hexID.c_str()); + task->set_status(status); + + const bool saveCreated = fs::create_save_data_for(user, saveMeta); + if (!saveCreated) + { + // Gonna borrow this too. + const char *popFailed = strings::get_by_name(strings::names::SAVECREATE_POPS, 1); + ui::PopMessageManager::push_message(ui::PopMessageManager::DEFAULT_TICKS, popFailed); + return false; + } + + // Need to reload and refresh to reflect changes. + user->clear_data_entries(); + user->load_user_data(); + MainMenuState::refresh_view_states(); + + return true; +} \ No newline at end of file diff --git a/source/tasks/useroptions.cpp b/source/tasks/useroptions.cpp index ff2808f..99a7dbc 100644 --- a/source/tasks/useroptions.cpp +++ b/source/tasks/useroptions.cpp @@ -90,7 +90,7 @@ void tasks::useroptions::backup_all_for_user_remote(sys::threadpool::JobData tas } data::TitleInfo *titleInfo = data::get_title_info_by_id(saveInfo->application_id); - if (error::is_null(saveInfo)) { continue; } + if (error::is_null(titleInfo)) { continue; } backupStruct->titleInfo = titleInfo; const std::string_view remoteTitle =