From fdf0fe7ec8374a7e61edad494bbcece18c25abbe Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Sun, 25 Oct 2026 12:39:52 +0000 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Read=20QDMI=20configuratio?= =?UTF-8?q?n=20with=20LLVM=20buffers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use one checked file read and preserve provider status codes and source paths. Remove the unused SC istream adapter while retaining the JSON text and filename entry points and existing exception build policy. Cover missing paths and ENOTDIR; validate the extracted change against main independently of the broader native error migration. Co-authored-by: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Signed-off-by: Lukas Burgholzer Assisted-by: GPT-6 via Codex --- .../qdmi/devices/sc/Configuration.hpp | 4 +- src/qdmi/common/DeviceConfiguration.cpp | 87 ++++--------------- src/qdmi/devices/sc/Configuration.cpp | 17 ---- test/qdmi/devices/sc/test_configuration.cpp | 27 +++--- test/qdmi/devices/sc/test_device.cpp | 43 ++++----- 5 files changed, 53 insertions(+), 125 deletions(-) diff --git a/include/mqt-core/qdmi/devices/sc/Configuration.hpp b/include/mqt-core/qdmi/devices/sc/Configuration.hpp index 66445fbbe7..5b0411700b 100644 --- a/include/mqt-core/qdmi/devices/sc/Configuration.hpp +++ b/include/mqt-core/qdmi/devices/sc/Configuration.hpp @@ -16,7 +16,6 @@ #include "llvm/Support/Error.h" #include -#include #include #include #include @@ -75,8 +74,7 @@ struct Device { /// Parse and validate a device description and report errors with @p source. [[nodiscard]] llvm::Expected readJSON(std::string_view json, std::string_view source); -[[nodiscard]] llvm::Expected -readJSON(std::istream& stream, std::string_view source = "input"); +/// Read a JSON file and return validation or file-access errors. [[nodiscard]] llvm::Expected readJSON(const std::string& path); } // namespace sc diff --git a/src/qdmi/common/DeviceConfiguration.cpp b/src/qdmi/common/DeviceConfiguration.cpp index 31e619e678..3b04ed7308 100644 --- a/src/qdmi/common/DeviceConfiguration.cpp +++ b/src/qdmi/common/DeviceConfiguration.cpp @@ -10,27 +10,24 @@ #include "qdmi/common/DeviceConfiguration.hpp" +#include "qdmi/common/Common.hpp" #include "qdmi/common/Diagnostics.hpp" #include "qdmi/device.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Process.h" -#include #include #include #include #include -#include -#include -#include #include #include #include #include #include -#include #ifdef _WIN32 #include @@ -103,77 +100,27 @@ namespace qdmi::detail { } namespace { -template -void reportPath(const std::filesystem::path& path, - const std::format_string<> fallback, - Reporter&& reporter) noexcept { - try { -#ifdef _WIN32 - const auto pathText = path.string(); - std::forward(reporter)(std::string_view(pathText)); -#else - std::forward(reporter)(std::string_view(path.native())); -#endif - } catch (...) { - qdmi::diagnostics::error(fallback); - } -} - [[nodiscard]] std::optional readFile(const std::filesystem::path& path, const bool bundled, int& status) { - std::error_code error; - const auto exists = std::filesystem::exists(path, error); - if (error) { - status = bundled ? QDMI_ERROR_FATAL : QDMI_ERROR_PERMISSIONDENIED; - reportPath(path, - "Cannot inspect QDMI device configuration (details unavailable)", - [&error](const std::string_view pathText) { - const auto message = error.message(); - qdmi::diagnostics::error( - "Cannot inspect QDMI device configuration '{}': {}", - pathText, message); - }); - return std::nullopt; - } - if (!exists) { - status = bundled ? QDMI_ERROR_FATAL : QDMI_ERROR_NOTFOUND; - reportPath(path, "QDMI device configuration does not exist", - [](const std::string_view pathText) { - qdmi::diagnostics::error( - "QDMI device configuration '{}' does not exist", pathText); - }); - return std::nullopt; - } - errno = 0; - std::ifstream input(path, std::ios::binary); - if (!input) { - status = bundled ? QDMI_ERROR_FATAL : QDMI_ERROR_PERMISSIONDENIED; - const auto errorNumber = errno; - reportPath(path, "Cannot read QDMI device configuration", - [errorNumber](const std::string_view pathText) { - const auto* const message = std::strerror(errorNumber); - qdmi::diagnostics::error( - "Cannot read QDMI device configuration '{}': {}", pathText, - message == nullptr ? "unknown error" : message); - }); - return std::nullopt; - } - std::string json{std::istreambuf_iterator(input), - std::istreambuf_iterator()}; - if (!input.eof() && input.fail()) { - status = bundled ? QDMI_ERROR_FATAL : QDMI_ERROR_PERMISSIONDENIED; - reportPath(path, "Failed while reading QDMI device configuration", - [](const std::string_view pathText) { - qdmi::diagnostics::error( - "Failed while reading QDMI device configuration '{}'", - pathText); - }); + const auto source = qdmi::detail::pathToString(path); + auto buffer = llvm::MemoryBuffer::getFile(source); + if (!buffer) { + if (bundled) { + status = QDMI_ERROR_FATAL; + } else if (buffer.getError() == std::errc::no_such_file_or_directory || + buffer.getError() == std::errc::not_a_directory) { + status = QDMI_ERROR_NOTFOUND; + } else { + status = QDMI_ERROR_PERMISSIONDENIED; + } + qdmi::diagnostics::error("Cannot read QDMI device configuration '{}': {}", + source, buffer.getError().message()); return std::nullopt; } status = QDMI_SUCCESS; return LoadedDeviceConfiguration{ - .json = std::move(json), - .source = path.string(), + .json = (*buffer)->getBuffer().str(), + .source = source, }; } } // namespace diff --git a/src/qdmi/devices/sc/Configuration.cpp b/src/qdmi/devices/sc/Configuration.cpp index 5b9d54060d..a5e9c86357 100644 --- a/src/qdmi/devices/sc/Configuration.cpp +++ b/src/qdmi/devices/sc/Configuration.cpp @@ -21,9 +21,6 @@ #include #include #include -#include -#include -#include #include #include #include @@ -314,18 +311,4 @@ llvm::Expected readJSON(const std::string& path) { } return readJSON((*buffer)->getBuffer(), path); } -llvm::Expected readJSON(std::istream& stream, std::string_view source) { - try { - const std::string json{std::istreambuf_iterator(stream), - std::istreambuf_iterator()}; - if (stream.bad()) { - return llvm::createStringError(llvm::StringRef(source) + - ": failed to read JSON stream"); - } - return readJSON(json, source); - } catch (const std::ios_base::failure& error) { - return llvm::createStringError(llvm::StringRef(source) + ": " + - error.what()); - } -} } // namespace sc diff --git a/test/qdmi/devices/sc/test_configuration.cpp b/test/qdmi/devices/sc/test_configuration.cpp index 5d2859ce54..221d027588 100644 --- a/test/qdmi/devices/sc/test_configuration.cpp +++ b/test/qdmi/devices/sc/test_configuration.cpp @@ -8,6 +8,7 @@ * Licensed under the MIT License */ +#include "qdmi/TestUtils.hpp" #include "qdmi/devices/sc/Configuration.hpp" #include "gmock/gmock-matchers.h" @@ -21,12 +22,11 @@ #include #include #include +#include #include -#include -#include #include -#include #include +#include #include #include @@ -363,19 +363,16 @@ TEST(ScConfigurationTest, AcceptsExplicitHigherAritySites) { (std::vector{2, 0, 1})); } -TEST(ScConfigurationTest, ReportsStreamReadFailures) { - class FailingBuffer final : public std::streambuf { - protected: - int_type underflow() override { - throw std::ios_base::failure("injected read failure"); - } - } buffer; - std::istream stream(&buffer); - auto parsed = readJSON(stream, "failing-stream"); +TEST(ScConfigurationTest, ReportsFileReadFailures) { + const mqt::test::TemporaryDirectory directory; + const auto path = (directory.path() / "missing.json").string(); + auto parsed = readJSON(path); ASSERT_FALSE(static_cast(parsed)); - const auto message = llvm::toString(parsed.takeError()); - EXPECT_THAT(message, testing::HasSubstr("failing-stream")); - EXPECT_THAT(message, testing::HasSubstr("injected read failure")); + auto error = parsed.takeError(); + llvm::visitErrors(error, [](const llvm::ErrorInfoBase& info) { + EXPECT_EQ(info.convertToErrorCode(), std::errc::no_such_file_or_directory); + }); + EXPECT_THAT(llvm::toString(std::move(error)), testing::HasSubstr(path)); } } // namespace sc diff --git a/test/qdmi/devices/sc/test_device.cpp b/test/qdmi/devices/sc/test_device.cpp index 06be930252..84967361d4 100644 --- a/test/qdmi/devices/sc/test_device.cpp +++ b/test/qdmi/devices/sc/test_device.cpp @@ -455,26 +455,29 @@ TEST(ScRuntimeConfiguration, SelectsEnvironmentAndExplicitFileSources) { } TEST(ScRuntimeConfiguration, MapsMissingExplicitFileToNotFound) { - MQT_SC_QDMI_Device_Session session = nullptr; - ASSERT_EQ(MQT_SC_QDMI_device_session_alloc(&session), QDMI_SUCCESS); - constexpr auto missing = - std::to_array("missing-sc-device-configuration.json"); - ASSERT_EQ(MQT_SC_QDMI_device_session_set_parameter( - session, QDMI_DEVICE_SESSION_PARAMETER_CUSTOM2, missing.size(), - missing.data()), - QDMI_SUCCESS); - testing::internal::CaptureStderr(); - const auto status = MQT_SC_QDMI_device_session_init(session); - const auto diagnostic = testing::internal::GetCapturedStderr(); - EXPECT_EQ(status, QDMI_ERROR_NOTFOUND); - EXPECT_THAT( - diagnostic, - testing::AllOf( - testing::HasSubstr("[mqt-core] [error]"), - testing::HasSubstr("QDMI device configuration " - "'missing-sc-device-configuration.json' does not " - "exist"))); - MQT_SC_QDMI_device_session_free(session); + const mqt::test::TemporaryDirectory directory; + const auto regularFile = directory.write("regular-file", ""); + for (const auto& path : { + directory.path() / "missing.json", + regularFile / "configuration.json", + }) { + const auto missing = path.string(); + SCOPED_TRACE(missing); + MQT_SC_QDMI_Device_Session session = nullptr; + ASSERT_EQ(MQT_SC_QDMI_device_session_alloc(&session), QDMI_SUCCESS); + ASSERT_EQ(MQT_SC_QDMI_device_session_set_parameter( + session, QDMI_DEVICE_SESSION_PARAMETER_CUSTOM2, + missing.size() + 1, missing.c_str()), + QDMI_SUCCESS); + testing::internal::CaptureStderr(); + const auto status = MQT_SC_QDMI_device_session_init(session); + const auto diagnostic = testing::internal::GetCapturedStderr(); + EXPECT_EQ(status, QDMI_ERROR_NOTFOUND); + EXPECT_THAT(diagnostic, + testing::AllOf(testing::HasSubstr("[mqt-core] [error]"), + testing::HasSubstr(missing))); + MQT_SC_QDMI_device_session_free(session); + } } TEST(ScRuntimeConfiguration, MissingCalibrationReturnsNotSupported) {