From 8533a595c49e0e083c2b4efd185bd74dc4ef5098 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 11 Aug 2025 18:16:15 +0000 Subject: [PATCH 01/21] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/pre-commit-hooks: v4.6.0 → v6.0.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.6.0...v6.0.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b8dc001c..982c265f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,7 +1,7 @@ fail_fast: false repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.6.0 + rev: v6.0.0 hooks: - id: trailing-whitespace - id: check-yaml From b451c7d4dffbd4cd1b153d4a379ca83877b97ef1 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 11 Aug 2025 18:27:47 +0000 Subject: [PATCH 02/21] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .vscode/extensions.json | 2 +- CMakePresets.json | 2 +- atom/CMakeLists.txt | 12 +- atom/algorithm/algorithm.cpp | 2 +- atom/algorithm/algorithm.hpp | 2 +- atom/algorithm/base.cpp | 2 +- atom/algorithm/base.hpp | 2 +- atom/algorithm/bignumber.cpp | 2 +- atom/algorithm/bignumber.hpp | 2 +- atom/algorithm/blowfish.cpp | 2 +- atom/algorithm/blowfish.hpp | 2 +- atom/algorithm/convolve.cpp | 6 +- atom/algorithm/flood.cpp | 2 +- atom/algorithm/flood.hpp | 2 +- atom/algorithm/fnmatch.cpp | 2 +- atom/algorithm/fnmatch.hpp | 2 +- atom/algorithm/fraction.cpp | 2 +- atom/algorithm/fraction.hpp | 2 +- atom/algorithm/huffman.hpp | 2 +- atom/algorithm/matrix_compress.cpp | 2 +- atom/algorithm/mhash.cpp | 18 +- atom/algorithm/pathfinding.cpp | 2 +- atom/algorithm/pathfinding.hpp | 2 +- atom/algorithm/perlin.hpp | 2 +- atom/algorithm/sha1.cpp | 2 +- atom/algorithm/sha1.hpp | 2 +- atom/algorithm/snowflake.hpp | 2 +- atom/algorithm/tea.cpp | 2 +- atom/algorithm/tea.hpp | 2 +- atom/algorithm/weight.hpp | 2 +- atom/algorithm/xmake.lua | 28 +- atom/async/async.hpp | 2 +- atom/async/async_executor.cpp | 2 +- atom/async/eventstack.hpp | 2 +- atom/async/lock.cpp | 56 +-- atom/async/lodash.hpp | 2 +- atom/async/message_bus.hpp | 6 +- atom/async/message_queue.hpp | 2 +- atom/async/parallel.hpp | 2 +- atom/async/pool.hpp | 2 +- atom/async/queue.hpp | 2 +- atom/async/timer.hpp | 2 +- atom/async/xmake.lua | 32 +- atom/components/CMakeLists.txt | 2 +- atom/components/dispatch.cpp | 2 +- atom/components/dispatch.hpp | 2 +- atom/components/xmake.lua | 30 +- atom/connection/async_sockethub.hpp | 2 +- atom/connection/async_tcpclient.cpp | 2 +- atom/connection/async_tcpclient.hpp | 2 +- atom/connection/async_udpclient.cpp | 2 +- atom/connection/async_udpserver.cpp | 2 +- atom/connection/async_udpserver.hpp | 2 +- atom/connection/fifoclient.hpp | 2 +- atom/connection/fifoserver.cpp | 2 +- atom/connection/fifoserver.hpp | 2 +- atom/connection/sockethub.cpp | 2 +- atom/connection/sockethub.hpp | 2 +- atom/connection/sshserver.cpp | 2 +- atom/connection/sshserver.hpp | 2 +- atom/connection/tcpclient.cpp | 202 +++++----- atom/connection/tcpclient.hpp | 2 +- atom/connection/ttybase.hpp | 2 +- atom/connection/udpclient.cpp | 2 +- atom/connection/udpclient.hpp | 2 +- atom/connection/udpserver.cpp | 2 +- atom/connection/udpserver.hpp | 2 +- atom/connection/xmake.lua | 52 +-- atom/containers/boost_containers.hpp | 2 +- atom/containers/graph.hpp | 2 +- atom/containers/high_performance.hpp | 2 +- atom/containers/intrusive.hpp | 62 +-- atom/containers/lockfree.hpp | 34 +- atom/error/CMakeLists.txt | 2 +- atom/error/stacktrace.cpp | 2 +- atom/error/stacktrace.hpp | 2 +- atom/error/xmake.lua | 28 +- atom/extra/asio/asio_compatibility.hpp | 2 +- atom/extra/asio/mqtt/client.cpp | 2 +- atom/extra/asio/mqtt/client.hpp | 2 +- atom/extra/asio/mqtt/packet.cpp | 2 +- atom/extra/asio/mqtt/packet.hpp | 2 +- atom/extra/asio/mqtt/protocol.hpp | 2 +- atom/extra/asio/mqtt/test_client.hpp | 2 +- atom/extra/asio/mqtt/test_packet.hpp | 2 +- atom/extra/asio/mqtt/test_protocol.hpp | 2 +- atom/extra/asio/mqtt/test_types.hpp | 2 +- atom/extra/asio/mqtt/types.hpp | 2 +- atom/extra/asio/sse/client/client.cpp | 2 +- atom/extra/asio/sse/client/client.hpp | 2 +- atom/extra/asio/sse/client/client_config.cpp | 2 +- atom/extra/asio/sse/client/client_config.hpp | 2 +- atom/extra/asio/sse/event.cpp | 2 +- atom/extra/asio/sse/event.hpp | 2 +- atom/extra/asio/sse/event_store.cpp | 2 +- atom/extra/asio/sse/event_store.hpp | 2 +- atom/extra/asio/sse/server/auth_service.cpp | 2 +- atom/extra/asio/sse/server/auth_service.hpp | 2 +- atom/extra/asio/sse/server/connection.cpp | 2 +- atom/extra/asio/sse/server/connection.hpp | 2 +- atom/extra/asio/sse/server/event_queue.cpp | 2 +- atom/extra/asio/sse/server/event_queue.hpp | 2 +- atom/extra/asio/sse/server/event_store.cpp | 2 +- atom/extra/asio/sse/server/event_store.hpp | 2 +- atom/extra/asio/sse/server/http_request.cpp | 2 +- atom/extra/asio/sse/server/http_request.hpp | 2 +- atom/extra/asio/sse/server/metrics.cpp | 2 +- atom/extra/asio/sse/server/metrics.hpp | 2 +- atom/extra/asio/sse/server/server.cpp | 2 +- atom/extra/asio/sse/server/server.hpp | 2 +- atom/extra/asio/sse/server/server_config.cpp | 2 +- atom/extra/asio/sse/server/server_config.hpp | 2 +- atom/extra/asio/sse/sse.hpp | 2 +- atom/extra/beast/ws.cpp | 2 +- atom/extra/beast/ws.hpp | 2 +- atom/extra/curl/cache.hpp | 2 +- atom/extra/curl/connection_pool.hpp | 2 +- atom/extra/curl/cookie.hpp | 2 +- atom/extra/curl/error.cpp | 2 +- atom/extra/curl/error.hpp | 2 +- atom/extra/curl/interceptor.hpp | 2 +- atom/extra/curl/multi_session.cpp | 2 +- atom/extra/curl/multi_session.hpp | 2 +- atom/extra/curl/multipart.cpp | 2 +- atom/extra/curl/multipart.hpp | 2 +- atom/extra/curl/rate_limiter.hpp | 2 +- atom/extra/curl/request.hpp | 2 +- atom/extra/curl/response.hpp | 2 +- atom/extra/curl/rest_client.hpp | 2 +- atom/extra/curl/session.hpp | 2 +- atom/extra/curl/session_pool.cpp | 2 +- atom/extra/curl/session_pool.hpp | 2 +- atom/extra/curl/websocket.hpp | 2 +- atom/extra/dotenv/CMakeLists.txt | 2 +- atom/extra/dotenv/dotenv.cpp | 2 +- atom/extra/dotenv/dotenv.hpp | 2 +- atom/extra/dotenv/exceptions.hpp | 2 +- atom/extra/dotenv/loader.cpp | 2 +- atom/extra/dotenv/loader.hpp | 2 +- atom/extra/dotenv/parser.cpp | 2 +- atom/extra/dotenv/parser.hpp | 2 +- atom/extra/dotenv/test_dotenv.hpp | 2 +- atom/extra/dotenv/test_validator.hpp | 2 +- atom/extra/dotenv/validator.cpp | 2 +- atom/extra/dotenv/validator.hpp | 2 +- atom/extra/iconv/test_iconv_cpp.cpp | 116 +++--- atom/extra/inicpp/event_listener.hpp | 2 +- atom/extra/inicpp/field.hpp | 6 +- atom/extra/inicpp/format_converter.hpp | 2 +- atom/extra/inicpp/inicpp.hpp | 4 +- atom/extra/inicpp/path_query.hpp | 2 +- atom/extra/inicpp/section.hpp | 22 +- atom/extra/pugixml/xml_builder.hpp | 2 +- atom/extra/pugixml/xml_document.hpp | 2 +- atom/extra/pugixml/xml_node_wrapper.hpp | 2 +- atom/extra/pugixml/xml_query.hpp | 2 +- atom/extra/spdlog/CMakeLists.txt | 2 +- atom/extra/spdlog/core/concepts.h | 2 +- atom/extra/spdlog/core/context.cpp | 2 +- atom/extra/spdlog/core/context.h | 2 +- atom/extra/spdlog/core/error.h | 2 +- atom/extra/spdlog/core/test_context.h | 2 +- atom/extra/spdlog/core/test_error.h | 2 +- atom/extra/spdlog/core/test_types.h | 2 +- atom/extra/spdlog/core/types.h | 2 +- atom/extra/spdlog/events/event_system.cpp | 2 +- atom/extra/spdlog/events/event_system.h | 2 +- .../extra/spdlog/events/test_event_system.cpp | 2 +- atom/extra/spdlog/filters/builtin_filters.cpp | 2 +- atom/extra/spdlog/filters/builtin_filters.h | 2 +- atom/extra/spdlog/filters/filter.cpp | 2 +- atom/extra/spdlog/filters/filter.h | 2 +- .../spdlog/filters/test_builtin_filters.cpp | 2 +- atom/extra/spdlog/logger/logger.cpp | 2 +- atom/extra/spdlog/logger/logger.h | 2 +- atom/extra/spdlog/logger/manager.cpp | 2 +- atom/extra/spdlog/logger/manager.h | 2 +- atom/extra/spdlog/logger/test_logger.cpp | 96 ++--- atom/extra/spdlog/logger/test_manager.cpp | 2 +- atom/extra/spdlog/modern_log.h | 2 +- atom/extra/spdlog/sampling/sampler.cpp | 2 +- atom/extra/spdlog/sampling/sampler.h | 2 +- atom/extra/spdlog/sampling/test_sampler.cpp | 2 +- atom/extra/spdlog/utils/archiver.cpp | 2 +- atom/extra/spdlog/utils/archiver.h | 2 +- atom/extra/spdlog/utils/structured_data.cpp | 2 +- atom/extra/spdlog/utils/structured_data.h | 2 +- atom/extra/spdlog/utils/test_archiver.cpp | 2 +- atom/extra/spdlog/utils/test_timer.cpp | 2 +- atom/extra/spdlog/utils/timer.cpp | 2 +- atom/extra/spdlog/utils/timer.h | 2 +- atom/extra/uv/coro.hpp | 2 +- atom/extra/uv/message_bus.cpp | 2 +- atom/extra/uv/message_bus.hpp | 2 +- atom/extra/uv/subprocess.cpp | 2 +- atom/extra/uv/subprocess.hpp | 2 +- atom/image/fits_data.hpp | 10 +- atom/image/fits_file.hpp | 14 +- atom/image/fits_header.cpp | 2 +- atom/image/fits_header.hpp | 10 +- atom/image/fits_utils.cpp | 2 +- atom/image/fits_utils.hpp | 2 +- atom/image/ocr/install_ocr_dependencies.sh | 184 ++++----- atom/image/ocr/ocr.cpp | 2 +- atom/image/ocr/ocr.hpp | 2 +- atom/image/ser/exception.h | 2 +- atom/image/ser/frame_processor.cpp | 38 +- atom/image/ser/frame_processor.h | 34 +- atom/image/ser/quality.cpp | 134 +++---- atom/image/ser/quality.h | 28 +- atom/image/ser/registration.h | 34 +- atom/image/ser/ser.hpp | 2 +- atom/image/ser/ser_format.h | 2 +- atom/image/ser/ser_reader.cpp | 2 +- atom/image/ser/ser_reader.h | 2 +- atom/image/ser/ser_writer.cpp | 2 +- atom/image/ser/ser_writer.h | 16 +- atom/image/ser/stacking.h | 30 +- atom/image/ser/utils.cpp | 2 +- atom/image/ser/utils.h | 8 +- atom/image/xmake.lua | 18 +- atom/io/compress.cpp | 34 +- atom/io/file_permission.cpp | 2 +- atom/io/file_permission.hpp | 2 +- atom/io/xmake.lua | 30 +- atom/log/async_logger.cpp | 2 +- atom/log/async_logger.hpp | 2 +- atom/log/atomlog.cpp | 2 +- atom/log/atomlog.hpp | 2 +- atom/log/log_manager.cpp | 2 +- atom/log/log_manager.hpp | 2 +- atom/log/mmap_logger.cpp | 2 +- atom/log/mmap_logger.hpp | 2 +- atom/log/xmake.lua | 20 +- atom/memory/memory_pool.hpp | 2 +- atom/memory/object.hpp | 2 +- atom/memory/ring.hpp | 2 +- atom/memory/shared.hpp | 2 +- atom/memory/tracker.hpp | 2 +- atom/memory/utils.hpp | 2 +- atom/memory/xmake.lua | 28 +- atom/meta/CMakeLists.txt | 2 +- atom/meta/container_traits.hpp | 2 +- atom/meta/facade.hpp | 2 +- atom/meta/facade_any.hpp | 2 +- atom/meta/facade_proxy.hpp | 2 +- atom/meta/field_count.hpp | 2 +- atom/meta/global_ptr.cpp | 2 +- atom/meta/god.hpp | 2 +- atom/meta/invoke.hpp | 2 +- atom/meta/proxy.hpp | 2 +- atom/meta/stepper.hpp | 2 +- atom/meta/type_info.hpp | 2 +- atom/meta/xmake.lua | 14 +- atom/search/lru.hpp | 2 +- atom/search/mysql.cpp | 2 +- atom/search/mysql.hpp | 170 ++++---- atom/search/search.cpp | 2 +- atom/search/sqlite.cpp | 2 +- atom/search/sqlite.hpp | 86 ++-- atom/search/ttl.hpp | 84 ++-- atom/search/xmake.lua | 20 +- atom/secret/CMakeLists.txt | 2 +- atom/secret/common.hpp | 2 +- atom/secret/encryption.cpp | 2 +- atom/secret/encryption.hpp | 2 +- atom/secret/password_entry.hpp | 2 +- atom/secret/result.hpp | 2 +- atom/secret/storage.cpp | 2 +- atom/secret/storage.hpp | 2 +- atom/secret/xmake.lua | 18 +- atom/serial/CMakeLists.txt | 10 +- atom/serial/bluetooth_serial.cpp | 2 +- atom/serial/bluetooth_serial.hpp | 2 +- atom/serial/bluetooth_serial_mac.hpp | 2 +- atom/serial/bluetooth_serial_mac.mm | 2 +- atom/serial/bluetooth_serial_unix.hpp | 2 +- atom/serial/bluetooth_serial_win.hpp | 2 +- atom/serial/scanner.cpp | 2 +- atom/serial/serial_port.cpp | 64 +-- atom/serial/serial_port.hpp | 2 +- atom/serial/serial_port_unix.hpp | 2 +- atom/serial/serial_port_win.hpp | 2 +- atom/serial/usb.hpp | 2 +- atom/serial/xmake.lua | 18 +- atom/sysinfo/CMakeLists.txt | 2 +- atom/sysinfo/cpu.hpp | 2 +- atom/sysinfo/cpu/freebsd.cpp | 274 ++++++------- atom/sysinfo/cpu/macos.cpp | 274 ++++++------- atom/sysinfo/disk.hpp | 4 +- atom/sysinfo/disk/disk_monitor.cpp | 6 +- atom/sysinfo/locale.hpp | 2 +- atom/sysinfo/memory.hpp | 2 +- atom/sysinfo/memory/CMakeLists.txt | 6 +- atom/sysinfo/memory/windows.cpp | 48 +-- atom/sysinfo/os.hpp | 46 +-- atom/sysinfo/sysinfo_printer.cpp | 48 +-- atom/sysinfo/sysinfo_printer.hpp | 78 ++-- atom/sysinfo/virtual.hpp | 2 +- atom/sysinfo/wifi/wifi.cpp | 6 +- atom/sysinfo/xmake.lua | 20 +- atom/system/CMakeLists.txt | 2 +- atom/system/clipboard.ipp | 16 +- atom/system/clipboard_error.hpp | 2 +- atom/system/clipboard_macos.cpp | 162 ++++---- atom/system/clipboard_windows.cpp | 8 +- atom/system/crontab.cpp | 2 +- atom/system/crontab.hpp | 2 +- atom/system/gpio.hpp | 2 +- atom/system/lregistry.cpp | 2 +- atom/system/lregistry.hpp | 2 +- atom/system/network_manager.hpp | 2 +- atom/system/nodebugger.cpp | 2 +- atom/system/nodebugger.hpp | 2 +- atom/system/pidwatcher.cpp | 2 +- atom/system/pidwatcher.hpp | 2 +- atom/system/shortcut/CMakeLists.txt | 8 +- atom/system/shortcut/detector.hpp | 2 +- atom/system/shortcut/detector_impl.cpp | 8 +- atom/system/shortcut/detector_impl.h | 14 +- atom/system/shortcut/factory.cpp | 2 +- atom/system/shortcut/factory.h | 2 +- atom/system/shortcut/shortcut.cpp | 2 +- atom/system/shortcut/shortcut.h | 2 +- atom/system/shortcut/status.h | 2 +- .../shortcut/test_shortcut_detector.cpp | 10 +- atom/system/shortcut/win32_utils.cpp | 8 +- atom/system/shortcut/win32_utils.h | 2 +- atom/system/software.hpp | 2 +- atom/system/stat.cpp | 2 +- atom/system/stat.hpp | 2 +- atom/system/storage.hpp | 2 +- atom/system/user.cpp | 6 +- atom/system/user.hpp | 2 +- atom/system/virtual_network.cpp | 2 +- atom/system/virtual_network.hpp | 2 +- atom/system/voltage.cpp | 2 +- atom/system/voltage.hpp | 2 +- atom/system/voltage_linux.cpp | 2 +- atom/system/voltage_linux.hpp | 2 +- atom/system/voltage_windows.cpp | 2 +- atom/system/voltage_windows.hpp | 2 +- atom/system/xmake.lua | 20 +- atom/tests/CMakeLists.txt | 2 +- atom/tests/benchmark.hpp | 4 +- atom/tests/charts.py | 8 +- atom/tests/perf.cpp | 2 +- atom/tests/perf.hpp | 2 +- atom/tests/test_cli.hpp | 2 +- atom/tests/test_registry.hpp | 2 +- atom/tests/test_reporter.hpp | 2 +- atom/tests/test_reporter_charts.hpp | 2 +- atom/tests/test_runner.hpp | 2 +- atom/type/args.hpp | 2 +- atom/type/argsview.hpp | 2 +- atom/type/auto_table.hpp | 2 +- atom/type/concurrent_map.hpp | 2 +- atom/type/concurrent_set.hpp | 2 +- atom/type/expected.hpp | 2 +- atom/type/json-schema.hpp | 2 +- atom/type/json.hpp | 2 +- atom/type/json_fwd.hpp | 2 +- atom/type/noncopyable.hpp | 2 +- atom/type/pod_vector.hpp | 2 +- atom/type/qvariant.hpp | 2 +- atom/type/robin_hood.hpp | 2 +- atom/type/ryaml.cpp | 2 +- atom/type/string.hpp | 2 +- atom/type/trackable.hpp | 2 +- atom/type/uint.hpp | 2 +- atom/type/weak_ptr.hpp | 2 +- atom/type/xmake.lua | 14 +- atom/utils/CMakeLists.txt | 2 +- atom/utils/aligned.hpp | 2 +- atom/utils/bit.hpp | 2 +- atom/utils/color_print.hpp | 2 +- atom/utils/convert.cpp | 2 +- atom/utils/convert.hpp | 2 +- atom/utils/lcg.cpp | 2 +- atom/utils/qprocess.cpp | 4 +- atom/utils/qtimer.cpp | 2 +- atom/utils/random.cpp | 2 +- atom/utils/simd_wrapper.hpp | 2 +- atom/utils/span.hpp | 2 +- atom/utils/stopwatcher.cpp | 2 +- atom/utils/stopwatcher.hpp | 2 +- atom/utils/switch.hpp | 2 +- atom/utils/time.cpp | 2 +- atom/utils/to_byte.hpp | 2 +- atom/utils/to_string.hpp | 12 +- atom/utils/valid_string.cpp | 2 +- atom/utils/xmake.lua | 16 +- atom/utils/xml.cpp | 2 +- atom/web/CMakeLists.txt | 4 +- atom/web/address/CMakeLists.txt | 2 +- atom/web/address/main.hpp | 6 +- atom/web/curl.hpp | 2 +- atom/web/downloader.hpp | 2 +- atom/web/minetype.hpp | 2 +- atom/web/time/xmake.lua | 10 +- atom/web/utils/dns.hpp | 2 +- atom/web/xmake.lua | 18 +- atom/xmake.lua | 16 +- build.bat | 12 +- build.sh | 14 +- cmake/ExamplesBuildOptions.cmake | 2 +- cmake/FindAsio.cmake | 6 +- cmake/FindReadline.cmake | 40 +- cmake/FindYamlCpp.cmake | 16 +- cmake/GitVersion.cmake | 40 +- cmake/PlatformSpecifics.cmake | 2 +- cmake/ScanModule.cmake | 80 ++-- cmake/TestsBuildOptions.cmake | 2 +- cmake/VcpkgSetup.cmake | 2 +- cmake/VersionConfig.cmake | 2 +- cmake/WSLDetection.cmake | 4 +- cmake/compiler_options.cmake | 96 ++--- cmake/module_dependencies.cmake | 2 +- cmake/version_info.h.in | 4 +- example/algorithm/CMakeLists.txt | 10 +- example/algorithm/algorithm.cpp | 2 +- example/algorithm/annealing.cpp | 2 +- example/algorithm/base.cpp | 2 +- example/algorithm/bignumber.cpp | 2 +- example/algorithm/convolve.cpp | 2 +- example/algorithm/error_calibration.cpp | 2 +- example/algorithm/flood.cpp | 2 +- example/algorithm/fnmatch.cpp | 2 +- example/algorithm/fraction.cpp | 2 +- example/algorithm/hash.cpp | 2 +- example/algorithm/huffman.cpp | 2 +- example/algorithm/math.cpp | 2 +- example/algorithm/matrix.cpp | 2 +- example/algorithm/matrix_compress.cpp | 2 +- example/algorithm/md5.cpp | 2 +- example/algorithm/mhash.cpp | 2 +- example/algorithm/perlin.cpp | 2 +- example/algorithm/rust_numeric.cpp | 2 +- example/algorithm/sha1.cpp | 2 +- example/algorithm/snowflake.cpp | 2 +- example/algorithm/tea.cpp | 2 +- example/algorithm/weight.cpp | 2 +- example/async/CMakeLists.txt | 10 +- example/async/async.cpp | 2 +- example/async/async_executor.cpp | 2 +- example/async/daemon.cpp | 14 +- example/async/eventstack.cpp | 2 +- example/async/future.cpp | 2 +- example/async/generator.cpp | 2 +- example/async/limiter.cpp | 2 +- example/async/lock.cpp | 2 +- example/async/message_bus.cpp | 2 +- example/async/message_queue.cpp | 2 +- example/async/packaged_task.cpp | 2 +- example/async/parallel.cpp | 54 +-- example/async/pool.cpp | 2 +- example/async/promise.cpp | 2 +- example/async/queue.cpp | 2 +- example/async/safetype.cpp | 2 +- example/async/slot.cpp | 2 +- example/async/thread_wrapper.cpp | 2 +- example/async/threadlocal.cpp | 2 +- example/async/timer.cpp | 2 +- example/async/trigger.cpp | 2 +- example/components/CMakeLists.txt | 10 +- example/connection/CMakeLists.txt | 10 +- example/connection/async_fifoclient.cpp | 2 +- example/connection/async_fifoserver.cpp | 2 +- example/connection/async_sockethub.cpp | 2 +- example/connection/async_tcpclient.cpp | 2 +- example/connection/async_udpclient.cpp | 2 +- example/connection/async_udpserver.cpp | 2 +- example/error/CMakeLists.txt | 10 +- example/error/exception.cpp | 2 +- example/error/stacktrace.cpp | 2 +- example/extra/CMakeLists.txt | 10 +- example/extra/beast/http.cpp | 2 +- example/extra/beast/ws.cpp | 2 +- example/extra/boost/charconv.cpp | 2 +- example/extra/boost/locale.cpp | 2 +- example/extra/boost/math.cpp | 2 +- example/extra/boost/regex.cpp | 2 +- example/extra/boost/system.cpp | 2 +- example/extra/boost/uuid.cpp | 2 +- example/extra/uv/subprocess.cpp | 2 +- example/image/CMakeLists.txt | 10 +- example/io/CMakeLists.txt | 10 +- example/log/CMakeLists.txt | 10 +- example/log/async_logger.cpp | 2 +- example/log/atomlog.cpp | 2 +- example/log/logger.cpp | 2 +- example/memory/CMakeLists.txt | 10 +- example/memory/memory.cpp | 2 +- example/memory/object.cpp | 2 +- example/memory/ring.cpp | 2 +- example/memory/shared.cpp | 2 +- example/memory/short_alloc.cpp | 2 +- example/memory/tracker.cpp | 2 +- example/memory/utils.cpp | 2 +- example/meta/CMakeLists.txt | 10 +- example/meta/abi.cpp | 2 +- example/meta/any.cpp | 2 +- example/meta/anymeta.cpp | 2 +- example/meta/bind_first.cpp | 2 +- example/meta/concept.cpp | 2 +- example/meta/constructor.cpp | 2 +- example/meta/conversion.cpp | 2 +- example/meta/decorate.cpp | 2 +- example/meta/enum.cpp | 2 +- example/meta/ffi.cpp | 4 +- example/meta/field_count.cpp | 2 +- example/meta/func_traits.cpp | 2 +- example/meta/global_ptr.cpp | 2 +- example/meta/god.cpp | 2 +- example/meta/invoke.cpp | 2 +- example/meta/member.cpp | 2 +- example/meta/overload.cpp | 2 +- example/meta/property.cpp | 2 +- example/meta/proxy.cpp | 2 +- example/meta/proxy_params.cpp | 2 +- example/meta/raw_name.cpp | 2 +- example/meta/signature.cpp | 2 +- example/meta/stepper.cpp | 2 +- example/meta/template_traits.cpp | 2 +- example/meta/type_caster.cpp | 2 +- example/meta/type_info.cpp | 2 +- example/meta/vany.cpp | 2 +- example/search/CMakeLists.txt | 10 +- example/search/cache.cpp | 2 +- example/search/lru.cpp | 2 +- example/search/search.cpp | 2 +- example/search/sqlite.cpp | 2 +- example/search/ttl.cpp | 2 +- example/serial/CMakeLists.txt | 10 +- example/serial/scanner.cpp | 58 +-- example/system/CMakeLists.txt | 10 +- example/system/command.cpp | 2 +- example/system/crash_quotes.cpp | 2 +- example/system/crontab.cpp | 2 +- example/system/env.cpp | 2 +- example/system/gpio.cpp | 2 +- example/system/lregistry.cpp | 2 +- example/system/network_manager.cpp | 2 +- example/system/pidwatcher.cpp | 2 +- example/system/priority.cpp | 2 +- example/system/process.cpp | 2 +- example/system/process_manager.cpp | 2 +- example/system/signal.cpp | 2 +- example/system/software.cpp | 2 +- example/system/stat.cpp | 2 +- example/system/storage.cpp | 2 +- example/system/user.cpp | 2 +- example/system/wregistry.cpp | 2 +- example/type/CMakeLists.txt | 10 +- example/type/args.cpp | 2 +- example/type/argsview.cpp | 2 +- example/type/auto_table.cpp | 2 +- example/type/concurrent_map.cpp | 2 +- example/type/concurrent_set.cpp | 2 +- example/type/concurrent_vector.cpp | 2 +- example/type/cstream.cpp | 2 +- example/type/expected.cpp | 2 +- example/type/flatmap.cpp | 2 +- example/type/flatset.cpp | 2 +- example/type/indestructible.cpp | 2 +- example/type/iter.cpp | 140 +++---- example/type/json-schema.cpp | 2 +- example/type/no_offset_ptr.cpp | 2 +- example/type/optional.cpp | 2 +- example/type/pod_vector.cpp | 2 +- example/type/pointer.cpp | 2 +- example/type/qvariant.cpp | 2 +- example/type/rtype.cpp | 2 +- example/type/small_list.cpp | 2 +- example/type/small_vector.cpp | 2 +- example/type/static_string.cpp | 2 +- example/type/static_vector.cpp | 2 +- example/type/string.cpp | 2 +- example/type/trackable.cpp | 2 +- example/type/uint.cpp | 2 +- example/type/weak_ptr.cpp | 286 +++++++------- example/utils/CMakeLists.txt | 10 +- example/utils/aes.cpp | 2 +- example/utils/aligned.cpp | 2 +- example/utils/anyutils.cpp | 2 +- example/utils/argsview.cpp | 2 +- example/utils/bit.cpp | 2 +- example/utils/container.cpp | 192 ++++----- example/utils/convert.cpp | 2 +- example/utils/cstring.cpp | 2 +- example/utils/difflib.cpp | 180 ++++----- example/utils/event_stack.cpp | 2 +- example/utils/lcg.cpp | 2 +- example/utils/leak.cpp | 130 +++--- example/utils/linq.cpp | 2 +- example/utils/print.cpp | 2 +- example/utils/qdatetime.cpp | 2 +- example/utils/qprocess.cpp | 2 +- example/utils/qtimer.cpp | 2 +- example/utils/qtimezone.cpp | 2 +- example/utils/random.cpp | 2 +- example/utils/ranges.cpp | 2 +- example/utils/span.cpp | 2 +- example/utils/stopwatcher.cpp | 2 +- example/utils/string.cpp | 2 +- example/utils/switch.cpp | 2 +- example/utils/time.cpp | 2 +- example/utils/to_any.cpp | 2 +- example/utils/to_byte.cpp | 2 +- example/utils/utf.cpp | 2 +- example/utils/uuid.cpp | 2 +- example/utils/valid_string.cpp | 2 +- example/utils/xml.cpp | 2 +- example/web/CMakeLists.txt | 10 +- example/web/address.cpp | 2 +- example/web/curl.cpp | 2 +- example/web/httpparser.cpp | 2 +- example/web/minetype.cpp | 2 +- example/web/time.cpp | 2 +- example/web/utils.cpp | 2 +- example/xmake.lua | 12 +- python/CMakeLists.txt | 8 +- python/algorithm/__init__.py | 2 +- python/algorithm/annealing.cpp | 56 +-- python/algorithm/base.cpp | 94 ++--- python/algorithm/blowfish.cpp | 82 ++-- python/algorithm/error_calibration.cpp | 90 ++--- python/algorithm/flood.cpp | 10 +- python/algorithm/fnmatch.cpp | 34 +- python/algorithm/fraction.cpp | 18 +- python/algorithm/hash.cpp | 88 ++--- python/algorithm/huffman.cpp | 106 ++--- python/algorithm/md5.cpp | 26 +- python/algorithm/mhash.cpp | 12 +- python/algorithm/pathfinding.cpp | 2 +- python/algorithm/perlin.cpp | 62 +-- python/algorithm/rust_numeric.cpp | 10 +- python/algorithm/sha1.cpp | 24 +- python/algorithm/snowflake.cpp | 16 +- python/algorithm/tea.cpp | 2 +- python/algorithm/weight.cpp | 40 +- python/async/__init__.py | 2 +- python/async/async.cpp | 30 +- python/async/async_executor.cpp | 24 +- python/async/daemon.cpp | 52 +-- python/async/eventstack.cpp | 94 ++--- python/async/future.cpp | 122 +++--- python/async/generator.cpp | 30 +- python/async/limiter.cpp | 20 +- python/async/message_bus.cpp | 36 +- python/async/message_queue.cpp | 66 ++-- python/async/packaged_task.cpp | 8 +- python/async/parallel.cpp | 2 +- python/async/pool.cpp | 4 +- python/async/promise.cpp | 4 +- python/async/queue.cpp | 2 +- python/async/safetype.cpp | 2 +- python/async/slot.cpp | 6 +- python/async/thread_wrapper.cpp | 2 +- python/async/threadlocal.cpp | 2 +- python/async/timer.cpp | 4 +- python/async/trigger.cpp | 4 +- python/connection/__init__.py | 2 +- python/connection/fifo.cpp | 2 +- python/connection/fifoserver.cpp | 2 +- python/connection/sockethub.cpp | 24 +- python/connection/tcpclient.cpp | 20 +- python/connection/udp.cpp | 4 +- python/connection/udpserver.cpp | 10 +- python/error/__init__.py | 2 +- python/error/stacktrace.cpp | 8 +- python/extra/beast/http.cpp | 8 +- python/extra/beast/http_utils.cpp | 4 +- python/extra/beast/ws.cpp | 10 +- python/extra/boost/charconv.cpp | 2 +- python/extra/boost/locale.cpp | 2 +- python/extra/boost/math.cpp | 8 +- python/extra/boost/regex.cpp | 2 +- python/extra/boost/system.cpp | 8 +- python/extra/boost/uuid.cpp | 4 +- python/io/__init__.py | 2 +- python/io/asyncio.cpp | 46 +-- python/io/compress.cpp | 2 +- python/io/dirstack.cpp | 46 +-- python/io/glob.cpp | 12 +- python/pybind11_json.hpp | 2 +- python/search/__init__.py | 2 +- python/search/cache.cpp | 2 +- python/search/lru.cpp | 2 +- python/search/mysql.cpp | 2 +- python/search/search.cpp | 2 +- python/search/sqlite.cpp | 2 +- python/search/ttl.cpp | 4 +- python/sysinfo/__init__.py | 2 +- python/sysinfo/battery.cpp | 42 +- python/sysinfo/bios.cpp | 10 +- python/sysinfo/cpu.cpp | 12 +- python/sysinfo/disk.cpp | 24 +- python/sysinfo/memory.cpp | 20 +- python/sysinfo/os.cpp | 2 +- python/sysinfo/sysinfo_printer.cpp | 10 +- python/sysinfo/wifi.cpp | 16 +- python/system/__init__.py | 2 +- python/system/command.cpp | 4 +- python/system/crash_quotes.cpp | 2 +- python/system/crontab.cpp | 12 +- python/system/env.cpp | 4 +- python/system/gpio.cpp | 2 +- python/system/pidwatcher.cpp | 2 +- python/system/power.cpp | 2 +- python/system/priority.cpp | 30 +- python/system/process.cpp | 2 +- python/system/process_info.cpp | 12 +- python/system/process_manager.cpp | 2 +- python/system/registry.cpp | 2 +- python/system/signal.cpp | 12 +- python/system/signal_monitor.cpp | 54 +-- python/system/signal_utils.cpp | 64 +-- python/system/stat.cpp | 4 +- python/system/storage.cpp | 14 +- python/system/user.cpp | 2 +- python/system/voltage.cpp | 8 +- python/system/wregistry.cpp | 4 +- python/type/__init__.py | 2 +- python/type/expected.cpp | 2 +- python/type/json_schema.cpp | 18 +- python/type/robin_hood.cpp | 2 +- python/type/trackable.cpp | 6 +- python/utils/__init__.py | 2 +- python/utils/aes.cpp | 2 +- python/utils/bit.cpp | 2 +- python/utils/difflib.cpp | 2 +- python/utils/error_stack.cpp | 12 +- python/utils/lcg.cpp | 64 +-- python/utils/linq.cpp | 2 +- python/utils/qdatetime.cpp | 2 +- python/utils/qprocess.cpp | 6 +- python/utils/qtimer.cpp | 6 +- python/utils/qtimezone.cpp | 2 +- python/utils/stopwatcher.cpp | 10 +- python/utils/time.cpp | 22 +- python/utils/uuid.cpp | 2 +- python/web/__init__.py | 2 +- python/web/address.cpp | 2 +- python/web/downloader.cpp | 2 +- python/web/httpparser.cpp | 6 +- python/web/mimetype.cpp | 2 +- python/web/utils.cpp | 2 +- scripts/setup_vcpkg.bat | 26 +- scripts/setup_vcpkg.ps1 | 32 +- tests/algorithm/test_blowfish.cpp | 2 +- tests/algorithm/test_fraction.cpp | 2 +- tests/algorithm/test_math.cpp | 2 +- tests/algorithm/test_mhash.cpp | 2 +- tests/algorithm/test_sha1.cpp | 2 +- tests/algorithm/test_tea.cpp | 2 +- tests/extra/beast/test_http.cpp | 2 +- tests/extra/beast/test_ws.cpp | 2 +- tests/extra/boost/test_charconv.hpp | 2 +- tests/extra/boost/test_locale.hpp | 2 +- tests/extra/boost/test_math.hpp | 2 +- tests/extra/boost/test_regex.hpp | 2 +- tests/extra/boost/test_system.hpp | 2 +- tests/extra/boost/test_uuid.hpp | 148 +++---- tests/extra/curl/test_rest_client.hpp | 2 +- tests/extra/inicpp/file.cpp | 2 +- tests/extra/pugixml/test_xml_builder.hpp | 2 +- tests/extra/pugixml/test_xml_document.hpp | 2 +- tests/extra/pugixml/test_xml_node_wrapper.hpp | 2 +- tests/extra/pugixml/test_xml_query.hpp | 2 +- tests/extra/uv/test_coro.hpp | 2 +- tests/extra/uv/test_message_bus.hpp | 2 +- tests/extra/uv/test_subprocess.hpp | 2 +- tests/image/test_fits_header.hpp | 94 ++--- tests/image/test_hdu.hpp | 224 +++++------ tests/image/test_image_blob.hpp | 140 +++---- tests/io/test_async_compress.cpp | 182 ++++----- tests/io/test_async_glob.cpp | 164 ++++---- tests/io/test_async_io.cpp | 374 +++++++++--------- tests/io/test_compress.cpp | 2 +- tests/io/test_file_permission.cpp | 2 +- tests/io/test_glob.cpp | 2 +- tests/io/test_pushd.cpp | 2 +- tests/memory/test_memory.cpp | 2 +- tests/memory/test_object.cpp | 2 +- tests/memory/test_ring.cpp | 2 +- tests/memory/test_shared.cpp | 2 +- tests/memory/test_short_alloc.cpp | 2 +- tests/memory/test_utils.cpp | 2 +- tests/meta/test_bind_first.hpp | 2 +- tests/meta/test_container_traits.hpp | 256 ++++++------ tests/meta/test_conversion.hpp | 2 +- tests/meta/test_decorate.cpp | 2 +- tests/meta/test_enum.hpp | 20 +- tests/meta/test_func_traits.hpp | 2 +- tests/meta/test_global_ptr.hpp | 2 +- tests/meta/test_god.hpp | 2 +- tests/meta/test_invoke.hpp | 2 +- tests/meta/test_member.cpp | 2 +- tests/meta/test_overload.hpp | 2 +- tests/meta/test_property.hpp | 2 +- tests/meta/test_proxy.hpp | 2 +- tests/meta/test_proxy_params.hpp | 2 +- tests/meta/test_raw_name.hpp | 2 +- tests/meta/test_signature.cpp | 2 +- tests/meta/test_stepper.hpp | 2 +- tests/meta/test_template_traits.hpp | 2 +- tests/meta/test_type_info.hpp | 2 +- tests/meta/test_vany.hpp | 2 +- tests/search/test_lru.hpp | 2 +- tests/search/test_ttl.hpp | 2 +- tests/serial/test_bluetooth_serial.hpp | 2 +- tests/serial/test_scanner.cpp | 2 +- tests/serial/test_serial_port.hpp | 174 ++++---- tests/serial/test_usb.cpp | 2 +- tests/system/test_command.cpp | 2 +- tests/system/test_crash_quotes.cpp | 2 +- tests/system/test_env.hpp | 2 +- tests/system/test_gpio.hpp | 2 +- tests/system/test_lregistry.hpp | 334 ++++++++-------- tests/system/test_network_manager.hpp | 90 ++--- tests/system/test_pidwatcher.hpp | 2 +- tests/system/test_stat.hpp | 2 +- tests/system/test_voltage.cpp | 104 ++--- tests/system/test_wregistry.cpp | 2 +- tests/type/test_auto_table.cpp | 2 +- tests/type/test_concurrent_set.hpp | 2 +- tests/type/test_expected.cpp | 2 +- tests/type/test_indestructible.hpp | 2 +- tests/type/test_iter.hpp | 2 +- tests/type/test_json-schema.hpp | 2 +- tests/type/test_no_offset_ptr.hpp | 6 +- tests/type/test_optional.hpp | 2 +- tests/type/test_pod_vector.cpp | 2 +- tests/type/test_pointer.hpp | 2 +- tests/type/test_rjson.cpp | 2 +- tests/type/test_robin_hood.hpp | 106 ++--- tests/type/test_rtype.hpp | 2 +- tests/type/test_ryaml.cpp | 2 +- tests/type/test_small_list.hpp | 26 +- tests/type/test_small_vector.hpp | 2 +- tests/type/test_static_string.hpp | 4 +- tests/type/test_static_vector.hpp | 2 +- tests/type/test_string.cpp | 2 +- tests/type/test_trackable.cpp | 2 +- tests/type/test_uint.cpp | 2 +- tests/type/test_weak_ptr.hpp | 2 +- tests/utils/test_aes.hpp | 2 +- tests/utils/test_aligned.hpp | 2 +- tests/utils/test_anyutils.hpp | 2 +- tests/utils/test_bit.hpp | 2 +- tests/utils/test_container.hpp | 2 +- tests/utils/test_cstring.hpp | 154 ++++---- tests/utils/test_difflib.hpp | 2 +- tests/utils/test_lcg.hpp | 2 +- tests/utils/test_linq.hpp | 2 +- tests/utils/test_print.hpp | 2 +- tests/utils/test_qdatetime.hpp | 2 +- tests/utils/test_qprocess.hpp | 2 +- tests/utils/test_qtimer.hpp | 2 +- tests/utils/test_random.hpp | 2 +- tests/utils/test_switch.hpp | 2 +- tests/utils/test_time.hpp | 2 +- tests/utils/test_to_byte.hpp | 2 +- tests/utils/test_to_string.hpp | 158 ++++---- tests/utils/test_uuid.cpp | 2 +- tests/utils/test_valid_string.hpp | 4 +- tests/utils/test_xml.hpp | 2 +- tests/web/test_address.hpp | 40 +- tests/web/test_httpparser.hpp | 2 +- tests/web/test_minetype.hpp | 2 +- tests/xmake.lua | 14 +- vcpkg.json | 2 +- xmake.lua | 10 +- 874 files changed, 5453 insertions(+), 5453 deletions(-) diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 1f274967..9a90fd32 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -7,4 +7,4 @@ "danielpinto8zz6.c-cpp-compile-run", "usernamehw.errorlens" ] -} \ No newline at end of file +} diff --git a/CMakePresets.json b/CMakePresets.json index 32073840..f073259a 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -286,4 +286,4 @@ } } ] -} \ No newline at end of file +} diff --git a/atom/CMakeLists.txt b/atom/CMakeLists.txt index 4f854b19..50c2543a 100644 --- a/atom/CMakeLists.txt +++ b/atom/CMakeLists.txt @@ -281,20 +281,20 @@ option(ATOM_BUILD_UNIFIED_LIBRARY "Build a unified Atom library containing all m if(ATOM_BUILD_UNIFIED_LIBRARY) # Get all targets that are atom modules get_property(ATOM_MODULE_TARGETS GLOBAL PROPERTY ATOM_MODULE_TARGETS) - + if(ATOM_MODULE_TARGETS) message(STATUS "Creating unified Atom library with modules: ${ATOM_MODULE_TARGETS}") - + # Create unified target add_library(atom-unified INTERFACE) - + # Link all module targets target_link_libraries(atom-unified INTERFACE ${ATOM_MODULE_TARGETS}) - + # Create an alias 'atom' that points to 'atom-unified' # This allows examples and other components to link against 'atom' add_library(atom ALIAS atom-unified) - + # Install unified target install(TARGETS atom-unified EXPORT atom-unified-targets @@ -305,4 +305,4 @@ if(ATOM_BUILD_UNIFIED_LIBRARY) endif() endif() -message(STATUS "Atom modules configuration completed successfully") \ No newline at end of file +message(STATUS "Atom modules configuration completed successfully") diff --git a/atom/algorithm/algorithm.cpp b/atom/algorithm/algorithm.cpp index fea8bbd5..66230465 100644 --- a/atom/algorithm/algorithm.cpp +++ b/atom/algorithm/algorithm.cpp @@ -694,4 +694,4 @@ void BoyerMoore::computeGoodSuffixShift() noexcept { spdlog::info("Good suffix shift table computed."); } -} // namespace atom::algorithm \ No newline at end of file +} // namespace atom::algorithm diff --git a/atom/algorithm/algorithm.hpp b/atom/algorithm/algorithm.hpp index 21df539b..f0297984 100644 --- a/atom/algorithm/algorithm.hpp +++ b/atom/algorithm/algorithm.hpp @@ -337,4 +337,4 @@ auto BloomFilter::elementCount() const noexcept } // namespace atom::algorithm -#endif \ No newline at end of file +#endif diff --git a/atom/algorithm/base.cpp b/atom/algorithm/base.cpp index 0bcc51b8..82454ffe 100644 --- a/atom/algorithm/base.cpp +++ b/atom/algorithm/base.cpp @@ -644,4 +644,4 @@ auto decodeBase32(std::string_view encoded_sv) noexcept } } -} // namespace atom::algorithm \ No newline at end of file +} // namespace atom::algorithm diff --git a/atom/algorithm/base.hpp b/atom/algorithm/base.hpp index fc6bff95..82606d4f 100644 --- a/atom/algorithm/base.hpp +++ b/atom/algorithm/base.hpp @@ -341,4 +341,4 @@ void parallelExecute(std::span data, size_t threadCount, } // namespace atom::algorithm -#endif \ No newline at end of file +#endif diff --git a/atom/algorithm/bignumber.cpp b/atom/algorithm/bignumber.cpp index c9c5d164..5264ddea 100644 --- a/atom/algorithm/bignumber.cpp +++ b/atom/algorithm/bignumber.cpp @@ -607,4 +607,4 @@ void BigNumber::validate() const { } } -} // namespace atom::algorithm \ No newline at end of file +} // namespace atom::algorithm diff --git a/atom/algorithm/bignumber.hpp b/atom/algorithm/bignumber.hpp index c68479ad..cad7218e 100644 --- a/atom/algorithm/bignumber.hpp +++ b/atom/algorithm/bignumber.hpp @@ -284,4 +284,4 @@ constexpr auto BigNumber::at(size_t index) const -> uint8_t { } // namespace atom::algorithm -#endif // ATOM_ALGORITHM_BIGNUMBER_HPP \ No newline at end of file +#endif // ATOM_ALGORITHM_BIGNUMBER_HPP diff --git a/atom/algorithm/blowfish.cpp b/atom/algorithm/blowfish.cpp index 49a4c482..8c771c3a 100644 --- a/atom/algorithm/blowfish.cpp +++ b/atom/algorithm/blowfish.cpp @@ -533,4 +533,4 @@ template void Blowfish::decrypt_data(std::span, usize&); template void Blowfish::decrypt_data(std::span, usize&); -} // namespace atom::algorithm \ No newline at end of file +} // namespace atom::algorithm diff --git a/atom/algorithm/blowfish.hpp b/atom/algorithm/blowfish.hpp index 685a9d52..79152766 100644 --- a/atom/algorithm/blowfish.hpp +++ b/atom/algorithm/blowfish.hpp @@ -132,4 +132,4 @@ class Blowfish { } // namespace atom::algorithm -#endif // ATOM_ALGORITHM_BLOWFISH_HPP \ No newline at end of file +#endif // ATOM_ALGORITHM_BLOWFISH_HPP diff --git a/atom/algorithm/convolve.cpp b/atom/algorithm/convolve.cpp index cf596b71..a508bb34 100644 --- a/atom/algorithm/convolve.cpp +++ b/atom/algorithm/convolve.cpp @@ -430,10 +430,10 @@ __kernel void convolve2D(__global const float* input, for (int j = -halfKernelCols; j <= halfKernelCols; ++j) { int x = clamp(row + i, 0, inputRows - 1); int y = clamp(col + j, 0, inputCols - 1); - + int kernelIdx = (i + halfKernelRows) * kernelCols + (j + halfKernelCols); int inputIdx = x * inputCols + y; - + sum += input[inputIdx] * kernel[kernelIdx]; } } @@ -1257,4 +1257,4 @@ auto applyGaussianFilter(const std::vector>& image, #ifdef _MSC_VER #pragma warning(pop) -#endif \ No newline at end of file +#endif diff --git a/atom/algorithm/flood.cpp b/atom/algorithm/flood.cpp index f7e95a20..c82f7592 100644 --- a/atom/algorithm/flood.cpp +++ b/atom/algorithm/flood.cpp @@ -373,4 +373,4 @@ usize FloodFill::processBlock( return filled_count; } -} // namespace atom::algorithm \ No newline at end of file +} // namespace atom::algorithm diff --git a/atom/algorithm/flood.hpp b/atom/algorithm/flood.hpp index aeea4ee2..8c3f20a6 100644 --- a/atom/algorithm/flood.hpp +++ b/atom/algorithm/flood.hpp @@ -694,4 +694,4 @@ usize FloodFill::fillParallel( } // namespace atom::algorithm -#endif // ATOM_ALGORITHM_FLOOD_GPP \ No newline at end of file +#endif // ATOM_ALGORITHM_FLOOD_GPP diff --git a/atom/algorithm/fnmatch.cpp b/atom/algorithm/fnmatch.cpp index 71c64044..5931f937 100644 --- a/atom/algorithm/fnmatch.cpp +++ b/atom/algorithm/fnmatch.cpp @@ -512,4 +512,4 @@ atom::algorithm::filter, std::vector>( const std::vector&, const std::vector&, int, bool); -} // namespace atom::algorithm \ No newline at end of file +} // namespace atom::algorithm diff --git a/atom/algorithm/fnmatch.hpp b/atom/algorithm/fnmatch.hpp index 45211e6f..196980a4 100644 --- a/atom/algorithm/fnmatch.hpp +++ b/atom/algorithm/fnmatch.hpp @@ -145,4 +145,4 @@ template } // namespace atom::algorithm -#endif // ATOM_SYSTEM_FNMATCH_HPP \ No newline at end of file +#endif // ATOM_SYSTEM_FNMATCH_HPP diff --git a/atom/algorithm/fraction.cpp b/atom/algorithm/fraction.cpp index 233e965a..4377b87d 100644 --- a/atom/algorithm/fraction.cpp +++ b/atom/algorithm/fraction.cpp @@ -450,4 +450,4 @@ auto makeFraction(double value, int max_denominator) -> Fraction { return Fraction(sign * h2, k2); } -} // namespace atom::algorithm \ No newline at end of file +} // namespace atom::algorithm diff --git a/atom/algorithm/fraction.hpp b/atom/algorithm/fraction.hpp index 8606d53f..4fcd1e4c 100644 --- a/atom/algorithm/fraction.hpp +++ b/atom/algorithm/fraction.hpp @@ -451,4 +451,4 @@ class Fraction { } // namespace atom::algorithm -#endif // ATOM_ALGORITHM_FRACTION_HPP \ No newline at end of file +#endif // ATOM_ALGORITHM_FRACTION_HPP diff --git a/atom/algorithm/huffman.hpp b/atom/algorithm/huffman.hpp index d626249d..9eb568f6 100644 --- a/atom/algorithm/huffman.hpp +++ b/atom/algorithm/huffman.hpp @@ -252,4 +252,4 @@ std::vector decompressParallel( } // namespace huffman_optimized -#endif // ATOM_ALGORITHM_HUFFMAN_HPP \ No newline at end of file +#endif // ATOM_ALGORITHM_HUFFMAN_HPP diff --git a/atom/algorithm/matrix_compress.cpp b/atom/algorithm/matrix_compress.cpp index 00f90b43..134fadf7 100644 --- a/atom/algorithm/matrix_compress.cpp +++ b/atom/algorithm/matrix_compress.cpp @@ -603,4 +603,4 @@ void performanceTest(i32 rows, i32 cols, bool runParallel) { } #endif -} // namespace atom::algorithm \ No newline at end of file +} // namespace atom::algorithm diff --git a/atom/algorithm/mhash.cpp b/atom/algorithm/mhash.cpp index 00d17996..dfd561b0 100644 --- a/atom/algorithm/mhash.cpp +++ b/atom/algorithm/mhash.cpp @@ -74,12 +74,12 @@ namespace { // Using template string to simplify OpenCL kernel code constexpr const char *minhashKernelSource = R"CLC( __kernel void minhash_kernel( - __global const size_t* hashes, - __global size_t* signature, - __global const size_t* a_values, - __global const size_t* b_values, - const size_t p, - const size_t num_hashes, + __global const size_t* hashes, + __global size_t* signature, + __global const size_t* a_values, + __global const size_t* b_values, + const size_t p, + const size_t num_hashes, const size_t num_elements ) { int gid = get_global_id(0); @@ -87,13 +87,13 @@ __kernel void minhash_kernel( size_t min_hash = SIZE_MAX; size_t a = a_values[gid]; size_t b = b_values[gid]; - + // Batch processing to leverage locality for (size_t i = 0; i < num_elements; ++i) { size_t h = (a * hashes[i] + b) % p; min_hash = (h < min_hash) ? h : min_hash; } - + signature[gid] = min_hash; } } @@ -628,4 +628,4 @@ auto keccak256(std::span input) -> std::array { thread_local std::vector tls_buffer_{}; -} // namespace atom::algorithm \ No newline at end of file +} // namespace atom::algorithm diff --git a/atom/algorithm/pathfinding.cpp b/atom/algorithm/pathfinding.cpp index e93d4b79..3fdb3bcf 100644 --- a/atom/algorithm/pathfinding.cpp +++ b/atom/algorithm/pathfinding.cpp @@ -652,4 +652,4 @@ std::vector PathFinder::funnelAlgorithm(const std::vector& path, return result; } -} // namespace atom::algorithm \ No newline at end of file +} // namespace atom::algorithm diff --git a/atom/algorithm/pathfinding.hpp b/atom/algorithm/pathfinding.hpp index 224a6406..65926501 100644 --- a/atom/algorithm/pathfinding.hpp +++ b/atom/algorithm/pathfinding.hpp @@ -523,4 +523,4 @@ struct hash { (hash()(p.y) << 1); } }; -} // namespace std \ No newline at end of file +} // namespace std diff --git a/atom/algorithm/perlin.hpp b/atom/algorithm/perlin.hpp index 3cd0f72f..bcc444bf 100644 --- a/atom/algorithm/perlin.hpp +++ b/atom/algorithm/perlin.hpp @@ -419,4 +419,4 @@ class PerlinNoise { }; } // namespace atom::algorithm -#endif // ATOM_ALGORITHM_PERLIN_HPP \ No newline at end of file +#endif // ATOM_ALGORITHM_PERLIN_HPP diff --git a/atom/algorithm/sha1.cpp b/atom/algorithm/sha1.cpp index a9e624e1..14d299dd 100644 --- a/atom/algorithm/sha1.cpp +++ b/atom/algorithm/sha1.cpp @@ -387,4 +387,4 @@ auto computeHashesInParallel(const Containers&... containers) return results; } -} // namespace atom::algorithm \ No newline at end of file +} // namespace atom::algorithm diff --git a/atom/algorithm/sha1.hpp b/atom/algorithm/sha1.hpp index 8a3208a0..25eccae2 100644 --- a/atom/algorithm/sha1.hpp +++ b/atom/algorithm/sha1.hpp @@ -265,4 +265,4 @@ template } // namespace atom::algorithm -#endif // ATOM_ALGORITHM_SHA1_HPP \ No newline at end of file +#endif // ATOM_ALGORITHM_SHA1_HPP diff --git a/atom/algorithm/snowflake.hpp b/atom/algorithm/snowflake.hpp index bd4f30a5..49b0cdd5 100644 --- a/atom/algorithm/snowflake.hpp +++ b/atom/algorithm/snowflake.hpp @@ -668,4 +668,4 @@ class Snowflake { } // namespace atom::algorithm -#endif // ATOM_ALGORITHM_SNOWFLAKE_HPP \ No newline at end of file +#endif // ATOM_ALGORITHM_SNOWFLAKE_HPP diff --git a/atom/algorithm/tea.cpp b/atom/algorithm/tea.cpp index a7abd41f..d1c8ea5c 100644 --- a/atom/algorithm/tea.cpp +++ b/atom/algorithm/tea.cpp @@ -421,4 +421,4 @@ template auto toUint32Vector>(const std::vector& data) template auto toByteArray>(const std::vector& data) -> std::vector; -} // namespace atom::algorithm \ No newline at end of file +} // namespace atom::algorithm diff --git a/atom/algorithm/tea.hpp b/atom/algorithm/tea.hpp index 44f2e78c..75c9b162 100644 --- a/atom/algorithm/tea.hpp +++ b/atom/algorithm/tea.hpp @@ -396,4 +396,4 @@ auto toByteArray(const Container &data) -> std::vector { } // namespace atom::algorithm -#endif \ No newline at end of file +#endif diff --git a/atom/algorithm/weight.hpp b/atom/algorithm/weight.hpp index e1744d96..e165819f 100644 --- a/atom/algorithm/weight.hpp +++ b/atom/algorithm/weight.hpp @@ -1147,4 +1147,4 @@ class WeightSelector { } // namespace atom::algorithm -#endif // ATOM_ALGORITHM_WEIGHT_HPP \ No newline at end of file +#endif // ATOM_ALGORITHM_WEIGHT_HPP diff --git a/atom/algorithm/xmake.lua b/atom/algorithm/xmake.lua index 8b88edbb..2f1f54e2 100644 --- a/atom/algorithm/xmake.lua +++ b/atom/algorithm/xmake.lua @@ -21,46 +21,46 @@ add_requires("openssl", "tbb", "loguru") target("atom-algorithm") -- Set target kind set_kind("static") - + -- Add source files (automatically collect .cpp files) add_files("*.cpp") - - -- Add header files (automatically collect .hpp files) + + -- Add header files (automatically collect .hpp files) add_headerfiles("*.hpp") - + -- Add include directories add_includedirs(".", {public = true}) - + -- Add packages add_packages("openssl", "tbb", "loguru") - + -- Add system libraries add_syslinks("pthread") - + -- Add dependencies (assuming they are other xmake targets or libraries) for _, dep in ipairs(atom_algorithm_depends) do add_deps(dep) end - + -- Set properties set_targetdir("$(buildir)/lib") set_objectdir("$(buildir)/obj") - + -- Enable position independent code for static library add_cxflags("-fPIC", {tools = {"gcc", "clang"}}) add_cflags("-fPIC", {tools = {"gcc", "clang"}}) - + -- Set version info set_version("1.0.0") - + -- Add compile features set_policy("build.optimization.lto", true) - + -- Installation rules after_build(function (target) -- Custom post-build actions if needed end) - + -- Install target on_install(function (target) local installdir = target:installdir() or "$(prefix)" @@ -80,7 +80,7 @@ if has_config("enable-deps-check") then -- Convert atom-error to ATOM_BUILD_ERROR format local dep_var = dep:upper():gsub("ATOM%-", "ATOM_BUILD_") if not has_config(dep_var:lower()) then - print("Warning: Module atom-algorithm depends on " .. dep .. + print("Warning: Module atom-algorithm depends on " .. dep .. ", but that module is not enabled for building") end end diff --git a/atom/async/async.hpp b/atom/async/async.hpp index 70915bc3..1c0e20b8 100644 --- a/atom/async/async.hpp +++ b/atom/async/async.hpp @@ -1541,4 +1541,4 @@ size_t AsyncWorkerManager::pruneCompletedWorkers() noexcept { } } } // namespace atom::async -#endif \ No newline at end of file +#endif diff --git a/atom/async/async_executor.cpp b/atom/async/async_executor.cpp index b836c53a..6d79d544 100644 --- a/atom/async/async_executor.cpp +++ b/atom/async/async_executor.cpp @@ -385,4 +385,4 @@ void AsyncExecutor::statsLoop(std::stop_token stoken) { } } -} // namespace atom::async \ No newline at end of file +} // namespace atom::async diff --git a/atom/async/eventstack.hpp b/atom/async/eventstack.hpp index 5bfd3b96..d84b672c 100644 --- a/atom/async/eventstack.hpp +++ b/atom/async/eventstack.hpp @@ -937,7 +937,7 @@ void EventStack::transformEvents(Func&& transformFunc) { Parallel::for_each(events_.begin(), events_.end(), std::forward(transformFunc)); */ - + } #endif } catch (const std::exception& e) { diff --git a/atom/async/lock.cpp b/atom/async/lock.cpp index 773bfe49..fff0eaf3 100644 --- a/atom/async/lock.cpp +++ b/atom/async/lock.cpp @@ -44,13 +44,13 @@ void Spinlock::lock() { // Slow path - exponential backoff uint32_t backoff_count = 1; constexpr uint32_t MAX_BACKOFF = 1024; - + while (true) { - // Perform exponential backoff + // Perform exponential backoff for (uint32_t i = 0; i < backoff_count; ++i) { cpu_relax(); } - + // Try to acquire the lock if (!flag_.test_and_set(std::memory_order_acquire)) { #ifdef ATOM_DEBUG @@ -58,10 +58,10 @@ void Spinlock::lock() { #endif return; } - + // Increase backoff time (capped at maximum) backoff_count = std::min(backoff_count * 2, MAX_BACKOFF); - + // Yield to scheduler if we've been spinning for a while if (backoff_count >= MAX_BACKOFF / 2) { std::this_thread::yield(); @@ -71,13 +71,13 @@ void Spinlock::lock() { auto Spinlock::tryLock() noexcept -> bool { bool success = !flag_.test_and_set(std::memory_order_acquire); - + #ifdef ATOM_DEBUG if (success) { owner_.store(std::this_thread::get_id(), std::memory_order_relaxed); } #endif - + return success; } @@ -90,9 +90,9 @@ void Spinlock::unlock() noexcept { } owner_.store(std::thread::id(), std::memory_order_relaxed); #endif - + flag_.clear(std::memory_order_release); - + #if defined(__cpp_lib_atomic_flag_test) // Use C++20's notify to wake waiting threads flag_.notify_one(); @@ -102,12 +102,12 @@ void Spinlock::unlock() noexcept { auto TicketSpinlock::lock() noexcept -> uint64_t { const auto ticket = ticket_.fetch_add(1, std::memory_order_acq_rel); auto current_serving = serving_.load(std::memory_order_acquire); - + // Fast path - check if we're next if (current_serving == ticket) { return ticket; } - + // Slow path with adaptive waiting strategy uint32_t spin_count = 0; while (true) { @@ -115,7 +115,7 @@ auto TicketSpinlock::lock() noexcept -> uint64_t { if (current_serving == ticket) { return ticket; } - + if (spin_count < MAX_SPIN_COUNT) { // Use CPU pause instruction for short spins cpu_relax(); @@ -137,7 +137,7 @@ void TicketSpinlock::unlock(uint64_t ticket) { throw std::invalid_argument("Incorrect ticket provided to unlock"); } #endif - + serving_.store(ticket + 1, std::memory_order_release); } @@ -146,23 +146,23 @@ void UnfairSpinlock::lock() noexcept { if (!flag_.test_and_set(std::memory_order_acquire)) { return; } - + // Slow path with backoff uint32_t backoff_count = 1; constexpr uint32_t MAX_BACKOFF = 1024; - + while (true) { for (uint32_t i = 0; i < backoff_count; ++i) { cpu_relax(); } - + if (!flag_.test_and_set(std::memory_order_acquire)) { return; } - + // Increase backoff time (capped at maximum) backoff_count = std::min(backoff_count * 2, MAX_BACKOFF); - + // Yield to scheduler if we've been spinning for a while if (backoff_count >= MAX_BACKOFF / 2) { std::this_thread::yield(); @@ -172,7 +172,7 @@ void UnfairSpinlock::lock() noexcept { void UnfairSpinlock::unlock() noexcept { flag_.clear(std::memory_order_release); - + #if defined(__cpp_lib_atomic_flag_test) // Wake any waiting threads (C++20 feature) flag_.notify_one(); @@ -202,7 +202,7 @@ void BoostSpinlock::lock() noexcept { // Slow path - exponential backoff uint32_t backoff_count = 1; constexpr uint32_t MAX_BACKOFF = 1024; - + // Wait until we acquire the lock while (true) { // First check if lock is free without doing an exchange @@ -215,15 +215,15 @@ void BoostSpinlock::lock() noexcept { return; } } - - // Perform exponential backoff + + // Perform exponential backoff for (uint32_t i = 0; i < backoff_count; ++i) { cpu_relax(); } - + // Increase backoff time (capped at maximum) backoff_count = std::min(backoff_count * 2, MAX_BACKOFF); - + // Yield to scheduler if we've been spinning for a while if (backoff_count >= MAX_BACKOFF / 2) { std::this_thread::yield(); @@ -233,16 +233,16 @@ void BoostSpinlock::lock() noexcept { auto BoostSpinlock::tryLock() noexcept -> bool { bool expected = false; - bool success = flag_.compare_exchange_strong(expected, true, + bool success = flag_.compare_exchange_strong(expected, true, boost::memory_order_acquire, boost::memory_order_relaxed); - + #ifdef ATOM_DEBUG if (success) { owner_.store(std::this_thread::get_id(), boost::memory_order_relaxed); } #endif - + return success; } @@ -255,7 +255,7 @@ void BoostSpinlock::unlock() noexcept { } owner_.store(std::thread::id(), boost::memory_order_relaxed); #endif - + flag_.store(false, boost::memory_order_release); } #endif diff --git a/atom/async/lodash.hpp b/atom/async/lodash.hpp index b4098e4b..983eb260 100644 --- a/atom/async/lodash.hpp +++ b/atom/async/lodash.hpp @@ -550,4 +550,4 @@ auto Throttle::callCount() const noexcept -> size_t { } } // namespace atom::async -#endif \ No newline at end of file +#endif diff --git a/atom/async/message_bus.hpp b/atom/async/message_bus.hpp index c50a6325..3fee523d 100644 --- a/atom/async/message_bus.hpp +++ b/atom/async/message_bus.hpp @@ -694,7 +694,7 @@ class MessageBus : public std::enable_shared_from_this { // Optimization: if 'once' subscribers are common, breaking here might be too early // if a token could somehow be associated with multiple names (not current design). // For now, assume a token is unique across all names for a given type. - // break; + // break; } } @@ -956,7 +956,7 @@ class MessageBus : public std::enable_shared_from_this { for (auto& subscriber : subscribersList) { // Iterate by reference to allow modification if needed (though not directly here) try { // Ensure message is converted to std::any for filter and handler - std::any msg_any = message; + std::any msg_any = message; if (subscriber.filter(msg_any) && calledSubscribers.insert(subscriber.token).second) { auto handler_task = [handlerFunc = subscriber.handler, message_for_handler = msg_any, token = subscriber.token]() { // Capture message_any by value try { @@ -998,7 +998,7 @@ class MessageBus : public std::enable_shared_from_this { subscribersList.end()); if (subscribersList.empty()) { // If list becomes empty, remove 'name' entry from typeIter->second - typeIter->second.erase(nameIterator); + typeIter->second.erase(nameIterator); if (typeIter->second.empty()) { // If type map becomes empty, remove type_index entry from subscribers_ subscribers_.erase(typeIter); diff --git a/atom/async/message_queue.hpp b/atom/async/message_queue.hpp index 2b41840a..0deade78 100644 --- a/atom/async/message_queue.hpp +++ b/atom/async/message_queue.hpp @@ -1114,4 +1114,4 @@ size_t MessageQueue::clearAllMessages() noexcept { } // namespace atom::async -#endif // ATOM_ASYNC_MESSAGE_QUEUE_HPP \ No newline at end of file +#endif // ATOM_ASYNC_MESSAGE_QUEUE_HPP diff --git a/atom/async/parallel.hpp b/atom/async/parallel.hpp index f0345b82..1b73f820 100644 --- a/atom/async/parallel.hpp +++ b/atom/async/parallel.hpp @@ -1432,4 +1432,4 @@ class SimdOps { } // namespace atom::async -#endif // ATOM_ASYNC_PARALLEL_HPP \ No newline at end of file +#endif // ATOM_ASYNC_PARALLEL_HPP diff --git a/atom/async/pool.hpp b/atom/async/pool.hpp index 5c566877..f6ef465e 100644 --- a/atom/async/pool.hpp +++ b/atom/async/pool.hpp @@ -1721,4 +1721,4 @@ auto asyncAsio(F&& f, Args&&... args) { } // namespace atom::async -#endif // ATOM_ASYNC_THREADPOOL_HPP \ No newline at end of file +#endif // ATOM_ASYNC_THREADPOOL_HPP diff --git a/atom/async/queue.hpp b/atom/async/queue.hpp index 1b8cc2a3..a3df0d63 100644 --- a/atom/async/queue.hpp +++ b/atom/async/queue.hpp @@ -1329,4 +1329,4 @@ class QueueBenchmark { } // namespace atom::async #endif // ATOM_QUEUE_BENCHMARK -#endif // ATOM_ASYNC_QUEUE_HPP \ No newline at end of file +#endif // ATOM_ASYNC_QUEUE_HPP diff --git a/atom/async/timer.hpp b/atom/async/timer.hpp index 570ea84d..4c04e3c5 100644 --- a/atom/async/timer.hpp +++ b/atom/async/timer.hpp @@ -466,4 +466,4 @@ void Timer::setCallback(Function &&func) noexcept(false) { } // namespace atom::async -#endif \ No newline at end of file +#endif diff --git a/atom/async/xmake.lua b/atom/async/xmake.lua index 47691dd3..c9bd4457 100644 --- a/atom/async/xmake.lua +++ b/atom/async/xmake.lua @@ -18,19 +18,19 @@ add_requires("loguru") target("atom-async") -- Set target kind set_kind("static") - + -- Add source files (explicitly specified) add_files("limiter.cpp", "lock.cpp", "timer.cpp") - + -- Add header files (explicitly specified) add_headerfiles( "async.hpp", - "daemon.hpp", + "daemon.hpp", "eventstack.hpp", "limiter.hpp", "lock.hpp", "message_bus.hpp", - "message_queue.hpp", + "message_queue.hpp", "pool.hpp", "queue.hpp", "safetype.hpp", @@ -38,33 +38,33 @@ target("atom-async") "timer.hpp", "trigger.hpp" ) - + -- Add include directories add_includedirs(".", {public = true}) - + -- Add packages add_packages("loguru") - + -- Add dependencies (assuming atom-utils is another xmake target) add_deps("atom-utils") - + -- Add system libraries add_syslinks("pthread") - + -- Enable position independent code for static library add_cxflags("-fPIC", {tools = {"gcc", "clang"}}) add_cflags("-fPIC", {tools = {"gcc", "clang"}}) - + -- Set target directory set_targetdir("$(buildir)/lib") set_objectdir("$(buildir)/obj") - + -- Set version info set_version("1.0.0") - + -- Set output name (equivalent to OUTPUT_NAME) set_basename("atom-async") - + -- Installation rules on_install(function (target) local installdir = target:installdir() or "$(prefix)" @@ -77,10 +77,10 @@ target("atom-async") -- Optional: Create an object library equivalent (if needed elsewhere) target("atom-async-object") set_kind("object") - + -- Add the same source files add_files("limiter.cpp", "lock.cpp", "timer.cpp") add_headerfiles( "async.hpp", - "daemon.hpp", - "eventstack.hpp", \ No newline at end of file + "daemon.hpp", + "eventstack.hpp", diff --git a/atom/components/CMakeLists.txt b/atom/components/CMakeLists.txt index 8663d60e..4a27a8ef 100644 --- a/atom/components/CMakeLists.txt +++ b/atom/components/CMakeLists.txt @@ -55,4 +55,4 @@ set_target_properties(${PROJECT_NAME} PROPERTIES # Install rules install(TARGETS ${PROJECT_NAME} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} -) \ No newline at end of file +) diff --git a/atom/components/dispatch.cpp b/atom/components/dispatch.cpp index 309befab..5684a669 100644 --- a/atom/components/dispatch.cpp +++ b/atom/components/dispatch.cpp @@ -709,4 +709,4 @@ auto CommandDispatcher::dispatchHelper(const std::string& name, THROW_INVALID_ARGUMENT( "No matching overload for command '{}' with the given arguments.", name); -} \ No newline at end of file +} diff --git a/atom/components/dispatch.hpp b/atom/components/dispatch.hpp index b3a56a65..58cfc568 100644 --- a/atom/components/dispatch.hpp +++ b/atom/components/dispatch.hpp @@ -635,4 +635,4 @@ auto CommandDispatcher::completeArgs(const Command& cmd, const ArgsType& args) return fullArgs; } -#endif \ No newline at end of file +#endif diff --git a/atom/components/xmake.lua b/atom/components/xmake.lua index 5d128da9..6675173e 100644 --- a/atom/components/xmake.lua +++ b/atom/components/xmake.lua @@ -27,7 +27,7 @@ add_requires("loguru") -- Define sources and headers local sources = { "component.cpp", - "dispatch.cpp", + "dispatch.cpp", "registry.cpp", "var.cpp" } @@ -43,38 +43,38 @@ local headers = { target("atom-component") -- Set target kind to shared library set_kind("shared") - + -- Add source files add_files(sources) - + -- Add header files add_headerfiles(headers) - + -- Add include directories add_includedirs(".", {public = true}) - + -- Add packages add_packages("loguru") - + -- Add dependencies (assuming these are other xmake targets) add_deps("atom-error", "atom-utils") - + -- Add system libraries add_syslinks("pthread") - + -- Enable position independent code (automatic for shared libraries) set_policy("build.optimization.lto", true) - + -- Set version info set_version("1.0.0") - + -- Set output name set_basename("atom-component") - + -- Set target and object directories set_targetdir("$(buildir)/lib") set_objectdir("$(buildir)/obj") - + -- Installation rules after_install(function (target) local installdir = target:installdir() or "$(prefix)" @@ -91,17 +91,17 @@ target("atom-component") -- Optional: Create object library target (equivalent to CMake's object library) target("atom-component-object") set_kind("object") - + -- Add the same source files add_files(sources) add_headerfiles(headers) - + -- Configuration add_includedirs(".") add_packages("loguru") add_deps("atom-error", "atom-utils") add_syslinks("pthread") - + -- Enable position independent code add_cxflags("-fPIC", {tools = {"gcc", "clang"}}) add_cflags("-fPIC", {tools = {"gcc", "clang"}}) diff --git a/atom/connection/async_sockethub.hpp b/atom/connection/async_sockethub.hpp index d6b2960e..7c74bc7b 100644 --- a/atom/connection/async_sockethub.hpp +++ b/atom/connection/async_sockethub.hpp @@ -143,4 +143,4 @@ class SocketHub { } // namespace atom::async::connection -#endif // ATOM_CONNECTION_ASYNC_SOCKETHUB_HPP \ No newline at end of file +#endif // ATOM_CONNECTION_ASYNC_SOCKETHUB_HPP diff --git a/atom/connection/async_tcpclient.cpp b/atom/connection/async_tcpclient.cpp index 9c20810e..34fc27f8 100644 --- a/atom/connection/async_tcpclient.cpp +++ b/atom/connection/async_tcpclient.cpp @@ -1189,4 +1189,4 @@ void TcpClient::setOnHeartbeatCallback(const OnHeartbeatCallback& callback) { impl_->setOnHeartbeatCallback(callback); } -} // namespace atom::async::connection \ No newline at end of file +} // namespace atom::async::connection diff --git a/atom/connection/async_tcpclient.hpp b/atom/connection/async_tcpclient.hpp index 7511f191..bf901d4b 100644 --- a/atom/connection/async_tcpclient.hpp +++ b/atom/connection/async_tcpclient.hpp @@ -318,4 +318,4 @@ class TcpClient { } // namespace atom::async::connection -#endif // ATOM_CONNECTION_ASYNC_TCPCLIENT_HPP \ No newline at end of file +#endif // ATOM_CONNECTION_ASYNC_TCPCLIENT_HPP diff --git a/atom/connection/async_udpclient.cpp b/atom/connection/async_udpclient.cpp index cb221746..bdb2dcef 100644 --- a/atom/connection/async_udpclient.cpp +++ b/atom/connection/async_udpclient.cpp @@ -807,4 +807,4 @@ UdpClient::Statistics UdpClient::getStatistics() const { void UdpClient::resetStatistics() { impl_->resetStatistics(); } -} // namespace atom::async::connection \ No newline at end of file +} // namespace atom::async::connection diff --git a/atom/connection/async_udpserver.cpp b/atom/connection/async_udpserver.cpp index 16bd3c0c..4371f537 100644 --- a/atom/connection/async_udpserver.cpp +++ b/atom/connection/async_udpserver.cpp @@ -799,4 +799,4 @@ template bool UdpSocketHub::setSocketOption(SocketOption option, template bool UdpSocketHub::setSocketOption( SocketOption option, const unsigned int& value); -} // namespace atom::async::connection \ No newline at end of file +} // namespace atom::async::connection diff --git a/atom/connection/async_udpserver.hpp b/atom/connection/async_udpserver.hpp index 87735735..32b0f989 100644 --- a/atom/connection/async_udpserver.hpp +++ b/atom/connection/async_udpserver.hpp @@ -226,4 +226,4 @@ class UdpSocketHub { } // namespace atom::async::connection -#endif \ No newline at end of file +#endif diff --git a/atom/connection/fifoclient.hpp b/atom/connection/fifoclient.hpp index d1cf71c4..a339318f 100644 --- a/atom/connection/fifoclient.hpp +++ b/atom/connection/fifoclient.hpp @@ -376,4 +376,4 @@ auto FifoClient::write(const T& data, } // namespace atom::connection -#endif // ATOM_CONNECTION_FIFOCLIENT_HPP \ No newline at end of file +#endif // ATOM_CONNECTION_FIFOCLIENT_HPP diff --git a/atom/connection/fifoserver.cpp b/atom/connection/fifoserver.cpp index 606fbe85..fb1ea7ad 100644 --- a/atom/connection/fifoserver.cpp +++ b/atom/connection/fifoserver.cpp @@ -953,4 +953,4 @@ void FIFOServer::setLogLevel(LogLevel level) { impl_->setLogLevel(level); } size_t FIFOServer::getQueueSize() const { return impl_->getQueueSize(); } -} // namespace atom::connection \ No newline at end of file +} // namespace atom::connection diff --git a/atom/connection/fifoserver.hpp b/atom/connection/fifoserver.hpp index c25cbb41..d4cdd7ac 100644 --- a/atom/connection/fifoserver.hpp +++ b/atom/connection/fifoserver.hpp @@ -326,4 +326,4 @@ class FIFOServer { } // namespace atom::connection -#endif // ATOM_CONNECTION_FIFOSERVER_HPP \ No newline at end of file +#endif // ATOM_CONNECTION_FIFOSERVER_HPP diff --git a/atom/connection/sockethub.cpp b/atom/connection/sockethub.cpp index 62c7141d..b2288ea2 100644 --- a/atom/connection/sockethub.cpp +++ b/atom/connection/sockethub.cpp @@ -850,4 +850,4 @@ void SocketHub::setClientTimeout(std::chrono::seconds timeout) { int SocketHub::getPort() const noexcept { return impl_->getPort(); } -} // namespace atom::connection \ No newline at end of file +} // namespace atom::connection diff --git a/atom/connection/sockethub.hpp b/atom/connection/sockethub.hpp index 40aa05ed..37f1c0bb 100644 --- a/atom/connection/sockethub.hpp +++ b/atom/connection/sockethub.hpp @@ -157,4 +157,4 @@ class SocketHub { } // namespace atom::connection -#endif // ATOM_CONNECTION_SOCKETHUB_HPP \ No newline at end of file +#endif // ATOM_CONNECTION_SOCKETHUB_HPP diff --git a/atom/connection/sshserver.cpp b/atom/connection/sshserver.cpp index 603afeaf..dabb9593 100644 --- a/atom/connection/sshserver.cpp +++ b/atom/connection/sshserver.cpp @@ -1364,4 +1364,4 @@ void SshServer::setServerVersion(const std::string& version) { impl_->setServerVersion(version); } -} // namespace atom::connection \ No newline at end of file +} // namespace atom::connection diff --git a/atom/connection/sshserver.hpp b/atom/connection/sshserver.hpp index 594b324d..f62dab19 100644 --- a/atom/connection/sshserver.hpp +++ b/atom/connection/sshserver.hpp @@ -533,4 +533,4 @@ class SshServer : public NonCopyable { } // namespace atom::connection -#endif // ATOM_CONNECTION_SSHSERVER_HPP \ No newline at end of file +#endif // ATOM_CONNECTION_SSHSERVER_HPP diff --git a/atom/connection/tcpclient.cpp b/atom/connection/tcpclient.cpp index e7e4d01f..63a0aefc 100644 --- a/atom/connection/tcpclient.cpp +++ b/atom/connection/tcpclient.cpp @@ -112,27 +112,27 @@ class TcpClient::Impl { cleanupResources(); } - type::expected connect(std::string_view host, + type::expected connect(std::string_view host, uint16_t port, std::chrono::milliseconds timeout) { try { if (port == 0) { return type::unexpected(std::system_error( - std::make_error_code(std::errc::invalid_argument), + std::make_error_code(std::errc::invalid_argument), "Invalid port number")); } // Resolve hostname struct addrinfo hints = {}; struct addrinfo* result = nullptr; - + hints.ai_family = options_.ipv6_enabled ? AF_UNSPEC : AF_INET; hints.ai_socktype = SOCK_STREAM; - + int status = getaddrinfo(std::string(host).c_str(), std::to_string(port).c_str(), &hints, &result); if (status != 0) { return type::unexpected(std::system_error( - std::make_error_code(std::errc::host_unreachable), + std::make_error_code(std::errc::host_unreachable), "Failed to resolve hostname: " + std::string(gai_strerror(status)))); } @@ -156,7 +156,7 @@ class TcpClient::Impl { // Attempt connection status = ::connect(socket_, rp->ai_addr, rp->ai_addrlen); - + #ifdef _WIN32 if (status == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK) { continue; // Try next address @@ -175,11 +175,11 @@ class TcpClient::Impl { // Verify connection success int error = 0; socklen_t len = sizeof(error); - if (getsockopt(socket_, SOL_SOCKET, SO_ERROR, + if (getsockopt(socket_, SOL_SOCKET, SO_ERROR, #ifdef _WIN32 reinterpret_cast(&error), #else - &error, + &error, #endif &len) < 0 || error != 0) { continue; // Try next address @@ -187,10 +187,10 @@ class TcpClient::Impl { // Restore blocking mode setNonBlocking(socket_, false); - + // Connection successful connected_ = true; - + #if defined(__linux__) // Add socket to epoll struct epoll_event event = {}; @@ -212,24 +212,24 @@ class TcpClient::Impl { if (onConnectedCallback_) { onConnectedCallback_(); } - + return {}; // Success } // If we got here, all connection attempts failed return type::unexpected(std::system_error( - std::make_error_code(std::errc::connection_refused), + std::make_error_code(std::errc::connection_refused), "Failed to connect to any resolved address")); } catch (const std::exception& e) { auto error = std::system_error( - std::make_error_code(std::errc::io_error), + std::make_error_code(std::errc::io_error), "Connection failed: " + std::string(e.what())); last_error_ = error; return type::unexpected(error); } } - Task> connect_async(std::string_view host, + Task> connect_async(std::string_view host, uint16_t port, std::chrono::milliseconds timeout) { auto result = connect(host, port, timeout); @@ -238,23 +238,23 @@ class TcpClient::Impl { void disconnect() { std::lock_guard lock(mutex_); - + if (connected_) { stopReceiving(); - + #ifdef _WIN32 closesocket(socket_); #else close(socket_); #endif connected_ = false; - + // Recreate socket for reuse socket_ = socket(options_.ipv6_enabled ? AF_INET6 : AF_INET, SOCK_STREAM, IPPROTO_TCP); if (socket_ >= 0) { configureSocket(); } - + // Invoke disconnection callback if (onDisconnectedCallback_) { onDisconnectedCallback_(); @@ -264,10 +264,10 @@ class TcpClient::Impl { type::expected send(std::span data) { std::lock_guard lock(mutex_); - + if (!connected_) { auto error = std::system_error( - std::make_error_code(std::errc::not_connected), + std::make_error_code(std::errc::not_connected), "Not connected"); last_error_ = error; return type::unexpected(error); @@ -281,21 +281,21 @@ class TcpClient::Impl { // Handle large data by sending in chunks size_t total_sent = 0; size_t remaining = data.size(); - + while (remaining > 0) { // Calculate chunk size (limited by SO_SNDBUF) size_t chunk_size = std::min(remaining, options_.send_buffer_size); - - ssize_t bytes_sent = ::send(socket_, - data.data() + total_sent, - chunk_size, + + ssize_t bytes_sent = ::send(socket_, + data.data() + total_sent, + chunk_size, #ifdef _WIN32 0 #else MSG_NOSIGNAL // Prevent SIGPIPE #endif ); - + if (bytes_sent < 0) { #ifdef _WIN32 if (WSAGetLastError() == WSAEWOULDBLOCK) { @@ -318,38 +318,38 @@ class TcpClient::Impl { continue; // Retry send } #endif - + auto error = createSystemError("Send failed"); last_error_ = error; return type::unexpected(error); } - + total_sent += bytes_sent; remaining -= bytes_sent; } - + return total_sent; } catch (const std::exception& e) { auto error = std::system_error( - std::make_error_code(std::errc::io_error), + std::make_error_code(std::errc::io_error), "Send operation failed: " + std::string(e.what())); last_error_ = error; return type::unexpected(error); } } - + Task> send_async(std::span data) { auto result = send(data); co_return result; } - type::expected, std::system_error> receive(size_t max_size, + type::expected, std::system_error> receive(size_t max_size, std::chrono::milliseconds timeout) { std::lock_guard lock(mutex_); - + if (!connected_) { auto error = std::system_error( - std::make_error_code(std::errc::not_connected), + std::make_error_code(std::errc::not_connected), "Not connected"); last_error_ = error; return type::unexpected(error); @@ -368,7 +368,7 @@ class TcpClient::Impl { // Wait until data is available or timeout if (!waitForReceiveReady(timeout)) { auto error = std::system_error( - std::make_error_code(std::errc::timed_out), + std::make_error_code(std::errc::timed_out), "Receive operation timed out"); last_error_ = error; return type::unexpected(error); @@ -377,10 +377,10 @@ class TcpClient::Impl { // Create buffer limited by max_size and receive buffer size size_t buffer_size = std::min(max_size, options_.receive_buffer_size); std::vector buffer(buffer_size); - + // Perform the receive ssize_t bytes_read = ::recv(socket_, buffer.data(), buffer_size, 0); - + if (bytes_read < 0) { auto error = createSystemError("Receive failed"); last_error_ = error; @@ -388,31 +388,31 @@ class TcpClient::Impl { } else if (bytes_read == 0) { // Connection closed by peer connected_ = false; - + if (onDisconnectedCallback_) { onDisconnectedCallback_(); } - + auto error = std::system_error( - std::make_error_code(std::errc::connection_reset), + std::make_error_code(std::errc::connection_reset), "Connection closed by peer"); last_error_ = error; return type::unexpected(error); } - + // Resize buffer to actual bytes read buffer.resize(bytes_read); return buffer; - + } catch (const std::exception& e) { auto error = std::system_error( - std::make_error_code(std::errc::io_error), + std::make_error_code(std::errc::io_error), "Receive operation failed: " + std::string(e.what())); last_error_ = error; return type::unexpected(error); } } - + Task, std::system_error>> receive_async( size_t max_size, std::chrono::milliseconds timeout) { auto result = receive(max_size, timeout); @@ -441,17 +441,17 @@ class TcpClient::Impl { void startReceiving(size_t buffer_size) { std::lock_guard lock(mutex_); - + if (!connected_) { return; } stopReceiving(); - + // Use at least the minimum buffer size size_t actual_buffer_size = std::max(buffer_size, options_.receive_buffer_size); receiving_stopped_.store(false); - + // Launch the receiving thread receiving_thread_ = std::jthread([this, actual_buffer_size](std::stop_token stop_token) { receiveLoop(actual_buffer_size, stop_token); @@ -460,7 +460,7 @@ class TcpClient::Impl { void stopReceiving() { receiving_stopped_.store(true); - + if (receiving_thread_.joinable()) { receiving_thread_.request_stop(); receiving_thread_.join(); @@ -475,25 +475,25 @@ class TcpClient::Impl { void configureSocket() { // Set socket options int opt = 1; - + // TCP keep-alive if (options_.keep_alive) { - setsockopt(socket_, SOL_SOCKET, SO_KEEPALIVE, + setsockopt(socket_, SOL_SOCKET, SO_KEEPALIVE, #ifdef _WIN32 reinterpret_cast(&opt), #else - &opt, + &opt, #endif sizeof(opt)); } - + // Disable Nagle's algorithm (TCP_NODELAY) if (options_.no_delay) { - setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, + setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, #ifdef _WIN32 reinterpret_cast(&opt), #else - &opt, + &opt, #endif sizeof(opt)); } @@ -501,20 +501,20 @@ class TcpClient::Impl { // Configure send and receive buffer sizes int recv_size = static_cast(options_.receive_buffer_size); int send_size = static_cast(options_.send_buffer_size); - - setsockopt(socket_, SOL_SOCKET, SO_RCVBUF, + + setsockopt(socket_, SOL_SOCKET, SO_RCVBUF, #ifdef _WIN32 reinterpret_cast(&recv_size), #else - &recv_size, + &recv_size, #endif sizeof(recv_size)); - setsockopt(socket_, SOL_SOCKET, SO_SNDBUF, + setsockopt(socket_, SOL_SOCKET, SO_SNDBUF, #ifdef _WIN32 reinterpret_cast(&send_size), #else - &send_size, + &send_size, #endif sizeof(send_size)); } @@ -539,7 +539,7 @@ class TcpClient::Impl { fd_set write_fds, error_fds; FD_ZERO(&write_fds); FD_ZERO(&error_fds); - + #ifdef _WIN32 FD_SET(socket_, &write_fds); FD_SET(socket_, &error_fds); @@ -547,79 +547,79 @@ class TcpClient::Impl { FD_SET(socket_, &write_fds); FD_SET(socket_, &error_fds); #endif - + struct timeval tv; tv.tv_sec = timeout.count() / 1000; tv.tv_usec = (timeout.count() % 1000) * 1000; - - int result = select(socket_ + 1, nullptr, &write_fds, &error_fds, + + int result = select(socket_ + 1, nullptr, &write_fds, &error_fds, timeout > std::chrono::milliseconds::zero() ? &tv : nullptr); - + return result > 0 && FD_ISSET(socket_, &write_fds); } bool waitForSendReady(std::chrono::milliseconds timeout) { fd_set write_fds; FD_ZERO(&write_fds); - + #ifdef _WIN32 FD_SET(socket_, &write_fds); #else FD_SET(socket_, &write_fds); #endif - + struct timeval tv; tv.tv_sec = timeout.count() / 1000; tv.tv_usec = (timeout.count() % 1000) * 1000; - + int result = select(socket_ + 1, nullptr, &write_fds, nullptr, timeout > std::chrono::milliseconds::zero() ? &tv : nullptr); - + return result > 0 && FD_ISSET(socket_, &write_fds); } bool waitForReceiveReady(std::chrono::milliseconds timeout) { fd_set read_fds; FD_ZERO(&read_fds); - + #ifdef _WIN32 FD_SET(socket_, &read_fds); #else FD_SET(socket_, &read_fds); #endif - + struct timeval tv; tv.tv_sec = timeout.count() / 1000; tv.tv_usec = (timeout.count() % 1000) * 1000; - + int result = select(socket_ + 1, &read_fds, nullptr, nullptr, timeout > std::chrono::milliseconds::zero() ? &tv : nullptr); - + return result > 0 && FD_ISSET(socket_, &read_fds); } void receiveLoop(size_t buffer_size, const std::stop_token& stop_token) { std::vector buffer(buffer_size); - + while (!receiving_stopped_.load() && !stop_token.stop_requested()) { try { #if defined(__linux__) // Use epoll for efficient I/O waiting on Linux struct epoll_event events[10]; int num_events = epoll_wait(epoll_fd_, events, 10, 100); - + if (num_events < 0) { if (errno == EINTR) continue; // Interrupted throw createSystemError("epoll_wait failed"); } - + bool has_data = false; for (int i = 0; i < num_events; i++) { if (events[i].events & EPOLLIN) { has_data = true; break; } - + if (events[i].events & (EPOLLERR | EPOLLHUP)) { // Socket error or hangup connected_ = false; @@ -629,23 +629,23 @@ class TcpClient::Impl { return; } } - + if (!has_data) { continue; // No data available } - + #elif defined(__APPLE__) // Use kqueue for efficient I/O waiting on macOS struct kevent events[10]; struct timespec timeout = {0, 100000000}; // 100ms - + int num_events = kevent(kqueue_fd_, nullptr, 0, events, 10, &timeout); - + if (num_events < 0) { if (errno == EINTR) continue; // Interrupted throw createSystemError("kevent failed"); } - + bool has_data = false; for (int i = 0; i < num_events; i++) { if (events[i].filter == EVFILT_READ) { @@ -653,11 +653,11 @@ class TcpClient::Impl { break; } } - + if (!has_data) { continue; // No data available } - + #else // Use select for other platforms if (!waitForReceiveReady(std::chrono::milliseconds(100))) { @@ -667,13 +667,13 @@ class TcpClient::Impl { // Lock for the recv operation std::unique_lock lock(mutex_); - + if (!connected_) { break; } - + ssize_t bytes_read = ::recv(socket_, buffer.data(), buffer.size(), 0); - + if (bytes_read < 0) { #ifdef _WIN32 if (WSAGetLastError() == WSAEWOULDBLOCK) { @@ -689,37 +689,37 @@ class TcpClient::Impl { // Connection closed connected_ = false; lock.unlock(); // Unlock before callback - + if (onDisconnectedCallback_) { onDisconnectedCallback_(); } break; } - + // Create a data view of valid size std::span data_view(buffer.data(), bytes_read); lock.unlock(); // Unlock before callback - + if (onDataReceivedCallback_) { onDataReceivedCallback_(data_view); } - + } catch (const std::system_error& e) { last_error_ = e; if (onErrorCallback_) { onErrorCallback_(e); } - + // If the error is fatal, break the loop if (e.code().value() != EINTR) { break; } } catch (const std::exception& e) { auto error = std::system_error( - std::make_error_code(std::errc::io_error), + std::make_error_code(std::errc::io_error), "Receive thread error: " + std::string(e.what())); last_error_ = error; - + if (onErrorCallback_) { onErrorCallback_(error); } @@ -730,7 +730,7 @@ class TcpClient::Impl { void cleanupResources() { stopReceiving(); - + if (socket_ >= 0) { #ifdef _WIN32 closesocket(socket_); @@ -773,18 +773,18 @@ class TcpClient::Impl { // Flags and options Options options_; std::atomic connected_{false}; - + // Threading support std::mutex mutex_; std::jthread receiving_thread_; std::atomic receiving_stopped_{false}; - + // Callbacks std::function onConnectedCallback_; std::function onDisconnectedCallback_; std::function)> onDataReceivedCallback_; std::function onErrorCallback_; - + // Error tracking std::system_error last_error_{std::error_code(), ""}; }; @@ -793,7 +793,7 @@ TcpClient::TcpClient(Options options) : impl_(std::make_unique(options)) { TcpClient::~TcpClient() = default; -type::expected TcpClient::connect(std::string_view host, +type::expected TcpClient::connect(std::string_view host, uint16_t port, std::chrono::milliseconds timeout) { auto result = impl_->connect(host, port, timeout); @@ -803,7 +803,7 @@ type::expected TcpClient::connect(std::string_view host return result; } -Task> TcpClient::connect_async(std::string_view host, +Task> TcpClient::connect_async(std::string_view host, uint16_t port, std::chrono::milliseconds timeout) { auto result = co_await impl_->connect_async(host, port, timeout); @@ -828,7 +828,7 @@ Task> TcpClient::send_async(std::span< co_return co_await impl_->send_async(data); } -type::expected, std::system_error> TcpClient::receive(size_t max_size, +type::expected, std::system_error> TcpClient::receive(size_t max_size, std::chrono::milliseconds timeout) { return impl_->receive(max_size, timeout); } @@ -858,4 +858,4 @@ const std::system_error& TcpClient::getLastError() const { return impl_->getLastError(); } -} // namespace atom::connection \ No newline at end of file +} // namespace atom::connection diff --git a/atom/connection/tcpclient.hpp b/atom/connection/tcpclient.hpp index 71b43d0a..eaa5f195 100644 --- a/atom/connection/tcpclient.hpp +++ b/atom/connection/tcpclient.hpp @@ -299,4 +299,4 @@ class TcpClient : public NonCopyable { } // namespace atom::connection -#endif // ATOM_CONNECTION_TCPCLIENT_HPP \ No newline at end of file +#endif // ATOM_CONNECTION_TCPCLIENT_HPP diff --git a/atom/connection/ttybase.hpp b/atom/connection/ttybase.hpp index ac51c2b0..9ec3c65c 100644 --- a/atom/connection/ttybase.hpp +++ b/atom/connection/ttybase.hpp @@ -203,4 +203,4 @@ auto makeByteSpan(Container& container) { std::ranges::size(container) * sizeof(value_type)); } -#endif // ATOM_CONNECTION_TTYBASE_HPP \ No newline at end of file +#endif // ATOM_CONNECTION_TTYBASE_HPP diff --git a/atom/connection/udpclient.cpp b/atom/connection/udpclient.cpp index e112c8eb..a4e2005f 100644 --- a/atom/connection/udpclient.cpp +++ b/atom/connection/udpclient.cpp @@ -1041,4 +1041,4 @@ bool UdpClient::isIPv6Supported() noexcept { return true; } -} // namespace atom::connection \ No newline at end of file +} // namespace atom::connection diff --git a/atom/connection/udpclient.hpp b/atom/connection/udpclient.hpp index 2cffe407..e25ee837 100644 --- a/atom/connection/udpclient.hpp +++ b/atom/connection/udpclient.hpp @@ -397,4 +397,4 @@ class UdpClient { }; } // namespace atom::connection -#endif // ATOM_CONNECTION_UDPCLIENT_HPP \ No newline at end of file +#endif // ATOM_CONNECTION_UDPCLIENT_HPP diff --git a/atom/connection/udpserver.cpp b/atom/connection/udpserver.cpp index 6f732849..b260023d 100644 --- a/atom/connection/udpserver.cpp +++ b/atom/connection/udpserver.cpp @@ -423,4 +423,4 @@ void UdpSocketHub::setBufferSize(std::size_t size) noexcept { impl_->setBufferSize(size); } -} // namespace atom::connection \ No newline at end of file +} // namespace atom::connection diff --git a/atom/connection/udpserver.hpp b/atom/connection/udpserver.hpp index 984c82e5..4f4b08f6 100644 --- a/atom/connection/udpserver.hpp +++ b/atom/connection/udpserver.hpp @@ -132,4 +132,4 @@ class UdpSocketHub { } // namespace atom::connection -#endif \ No newline at end of file +#endif diff --git a/atom/connection/xmake.lua b/atom/connection/xmake.lua index 41b85b52..4af10d5f 100644 --- a/atom/connection/xmake.lua +++ b/atom/connection/xmake.lua @@ -44,7 +44,7 @@ option_end() -- Define base sources and headers local base_sources = { "async_fifoclient.cpp", - "async_fifoserver.cpp", + "async_fifoserver.cpp", "async_sockethub.cpp", "async_tcpclient.cpp", "async_udpclient.cpp", @@ -53,14 +53,14 @@ local base_sources = { "fifoserver.cpp", "sockethub.cpp", "tcpclient.cpp", - "udpclient.cpp", + "udpclient.cpp", "udpserver.cpp" } local base_headers = { "async_fifoclient.hpp", "async_fifoserver.hpp", - "async_sockethub.hpp", + "async_sockethub.hpp", "async_tcpclient.hpp", "async_udpclient.hpp", "async_udpserver.hpp", @@ -79,7 +79,7 @@ local ssh_sources = { } local ssh_headers = { - "sshclient.hpp", + "sshclient.hpp", "sshserver.hpp" } @@ -87,65 +87,65 @@ local ssh_headers = { target("atom-connection") -- Set target kind set_kind("static") - + -- Add base source files add_files(base_sources) add_headerfiles(base_headers) - + -- Add SSH files conditionally if has_config("enable-libssh") then add_files(ssh_sources) add_headerfiles(ssh_headers) end - + -- Add include directories add_includedirs(".", {public = true}) - + -- Add packages add_packages("loguru", "openssl") - + -- Add SSH package conditionally if has_config("enable-ssh") then add_packages("libssh") end - + -- Add system libraries add_syslinks("pthread") - + -- Windows-specific libraries if is_plat("windows") then add_syslinks("ws2_32", "mswsock") end - + -- Enable position independent code add_cxflags("-fPIC", {tools = {"gcc", "clang"}}) add_cflags("-fPIC", {tools = {"gcc", "clang"}}) - + -- Set version info set_version("1.0.0") - + -- Set output name set_basename("atom-connection") - + -- Set directories set_targetdir("$(buildir)/lib") set_objectdir("$(buildir)/obj") - + -- Installation rules after_install(function (target) local installdir = target:installdir() or "$(prefix)" -- Install static library os.cp(target:targetfile(), path.join(installdir, "lib")) - + -- Install headers local headerdir = path.join(installdir, "include", "atom-connection") os.mkdir(headerdir) - + -- Install base headers for _, header in ipairs(base_headers) do os.cp(header, headerdir) end - + -- Install SSH headers conditionally if has_config("enable-libssh") then for _, header in ipairs(ssh_headers) do @@ -157,31 +157,31 @@ target("atom-connection") -- Optional: Create object library target target("atom-connection-object") set_kind("object") - + -- Add base files add_files(base_sources) add_headerfiles(base_headers) - + -- Add SSH files conditionally if has_config("enable-libssh") then add_files(ssh_sources) add_headerfiles(ssh_headers) end - + -- Configuration add_includedirs(".") add_packages("loguru", "openssl") - + if has_config("enable-ssh") then add_packages("libssh") end - + add_syslinks("pthread") - + if is_plat("windows") then add_syslinks("ws2_32", "mswsock") end - + -- Enable PIC add_cxflags("-fPIC", {tools = {"gcc", "clang"}}) add_cflags("-fPIC", {tools = {"gcc", "clang"}}) diff --git a/atom/containers/boost_containers.hpp b/atom/containers/boost_containers.hpp index 5eea0f8b..2b01631a 100644 --- a/atom/containers/boost_containers.hpp +++ b/atom/containers/boost_containers.hpp @@ -126,4 +126,4 @@ using flat_map = boost::container::flat_map< } // namespace containers } // namespace atom -#endif // defined(ATOM_HAS_BOOST_CONTAINER) \ No newline at end of file +#endif // defined(ATOM_HAS_BOOST_CONTAINER) diff --git a/atom/containers/graph.hpp b/atom/containers/graph.hpp index bafa14ce..e0f0afd1 100644 --- a/atom/containers/graph.hpp +++ b/atom/containers/graph.hpp @@ -549,4 +549,4 @@ Graph create_graph( } // namespace containers } // namespace atom -#endif // defined(ATOM_HAS_BOOST_GRAPH) \ No newline at end of file +#endif // defined(ATOM_HAS_BOOST_GRAPH) diff --git a/atom/containers/high_performance.hpp b/atom/containers/high_performance.hpp index dc761e4a..6bb7fbd8 100644 --- a/atom/containers/high_performance.hpp +++ b/atom/containers/high_performance.hpp @@ -507,4 +507,4 @@ using String = std::string; #endif // ATOM_OPTIMIZE_FOR_SPEED -} // namespace atom::containers \ No newline at end of file +} // namespace atom::containers diff --git a/atom/containers/intrusive.hpp b/atom/containers/intrusive.hpp index eda77e6b..2bb3869e 100644 --- a/atom/containers/intrusive.hpp +++ b/atom/containers/intrusive.hpp @@ -38,17 +38,17 @@ using slist_base_hook = boost::intrusive::slist_base_hook<>; /** * @brief 侵入式链表 - * + * * 侵入式链表要求元素类型内包含钩子(hook),避免了额外的内存分配。 * 非常适合管理大量对象,减少内存碎片和提高缓存性能。 - * + * * 使用示例: * class MyClass : public atom::containers::intrusive::list_base_hook { * // 类成员和方法 * }; - * + * * atom::containers::intrusive::list my_list; - * + * * @tparam T 必须继承自list_base_hook的元素类型 */ template @@ -56,9 +56,9 @@ using list = boost::intrusive::list; /** * @brief 侵入式单向链表 - * + * * 比双向链表更轻量,但只支持单向遍历 - * + * * @tparam T 必须继承自slist_base_hook的元素类型 */ template @@ -66,9 +66,9 @@ using slist = boost::intrusive::slist; /** * @brief 侵入式有序集合 - * + * * 元素按键排序,提供快速查找,同时避免了内存分配开销 - * + * * @tparam T 必须继承自set_base_hook的元素类型 * @tparam Compare 比较元素的函数对象类型 */ @@ -77,14 +77,14 @@ using set = boost::intrusive::set>; /** * @brief 侵入式无序集合 - * + * * 通过哈希实现快速查找,避免了标准无序容器的节点分配开销 - * + * * @tparam T 必须继承自unordered_set_base_hook的元素类型 * @tparam Hash 哈希函数对象类型 * @tparam Equal 判断元素相等的函数对象类型 */ -template , typename Equal = std::equal_to> class unordered_set { @@ -93,80 +93,80 @@ class unordered_set { static constexpr std::size_t NumBuckets = 128; using bucket_type = boost::intrusive::unordered_set::bucket_type; bucket_type buckets_[NumBuckets]; - + using unordered_set_type = boost::intrusive::unordered_set< T, boost::intrusive::hash, boost::intrusive::equal, boost::intrusive::constant_time_size >; - + unordered_set_type set_; - + public: using iterator = typename unordered_set_type::iterator; using const_iterator = typename unordered_set_type::const_iterator; - + unordered_set() : set_(boost::intrusive::bucket_traits(buckets_, NumBuckets)) {} - + /** * @brief 插入元素到无序集合 - * + * * @param value 要插入的元素 * @return std::pair 包含指向插入元素的迭代器和是否成功插入的标志 */ std::pair insert(T& value) { return set_.insert(value); } - + /** * @brief 从无序集合中移除元素 - * + * * @param value 要移除的元素 * @return bool 如果元素被移除则返回true */ bool remove(T& value) { return set_.erase(value) > 0; } - + /** * @brief 查找元素 - * + * * @param value 要查找的元素 * @return iterator 指向找到的元素,如果未找到则返回end() */ iterator find(const T& value) { return set_.find(value); } - + /** * @brief 返回起始迭代器 */ iterator begin() { return set_.begin(); } - + /** * @brief 返回终止迭代器 */ iterator end() { return set_.end(); } - + /** * @brief 检查容器是否为空 */ bool empty() const { return set_.empty(); } - + /** * @brief 返回容器中元素的数量 */ std::size_t size() const { return set_.size(); } - + /** * @brief 清空容器 */ @@ -177,7 +177,7 @@ class unordered_set { /** * @brief 提供可链接类型的助手基类 - * + * * 这个类简化了创建支持多种侵入式容器的对象。 * 如果需要一个对象同时可以放入list、set和unordered_set, * 可以继承这个类。 @@ -191,14 +191,14 @@ class intrusive_base : protected: // 保护构造函数防止直接实例化 intrusive_base() = default; - + // 允许派生类销毁 virtual ~intrusive_base() = default; - + // 禁止复制 intrusive_base(const intrusive_base&) = delete; intrusive_base& operator=(const intrusive_base&) = delete; - + // 允许移动 intrusive_base(intrusive_base&&) = default; intrusive_base& operator=(intrusive_base&&) = default; @@ -208,4 +208,4 @@ class intrusive_base : } // namespace containers } // namespace atom -#endif // defined(ATOM_HAS_BOOST_INTRUSIVE) \ No newline at end of file +#endif // defined(ATOM_HAS_BOOST_INTRUSIVE) diff --git a/atom/containers/lockfree.hpp b/atom/containers/lockfree.hpp index b0b6127c..fbe29449 100644 --- a/atom/containers/lockfree.hpp +++ b/atom/containers/lockfree.hpp @@ -33,7 +33,7 @@ namespace lockfree { * * 这个队列允许多个线程并发地入队和出队,无需互斥锁。 * 适用于高性能并发系统和并行计算。 - * + * * @tparam T 元素类型 * @tparam Capacity 队列容量 */ @@ -47,7 +47,7 @@ class queue { /** * @brief 将元素推入队列 - * + * * @param item 要入队的元素 * @return bool 如果成功返回true,如果队列已满则返回false */ @@ -57,7 +57,7 @@ class queue { /** * @brief 从队列弹出元素 - * + * * @param item 接收弹出元素的引用 * @return bool 如果成功返回true,如果队列为空则返回false */ @@ -67,9 +67,9 @@ class queue { /** * @brief 检查队列是否为空 - * + * * 注意:在多线程环境中,此操作结果可能立即过期 - * + * * @return bool 如果队列为空返回true */ bool empty() const { @@ -79,10 +79,10 @@ class queue { /** * @brief 单生产者单消费者无锁队列 - * + * * 这个高度优化的队列适用于只有一个线程生产数据和一个线程消费数据的场景。 * 比多生产者多消费者版本有更低的开销。 - * + * * @tparam T 元素类型 * @tparam Capacity 队列容量 */ @@ -96,7 +96,7 @@ class spsc_queue { /** * @brief 将元素推入队列 - * + * * @param item 要入队的元素 * @return bool 如果成功返回true,如果队列已满则返回false */ @@ -106,7 +106,7 @@ class spsc_queue { /** * @brief 从队列弹出元素 - * + * * @param item 接收弹出元素的引用 * @return bool 如果成功返回true,如果队列为空则返回false */ @@ -116,7 +116,7 @@ class spsc_queue { /** * @brief 检查队列是否为空 - * + * * @return bool 如果队列为空返回true */ bool empty() const { @@ -126,9 +126,9 @@ class spsc_queue { /** * @brief 无锁栈 - * + * * 线程安全的LIFO数据结构,允许多个线程并发地压入和弹出元素,无需互斥锁。 - * + * * @tparam T 元素类型 * @tparam Capacity 栈容量 */ @@ -142,7 +142,7 @@ class stack { /** * @brief 将元素压入栈 - * + * * @param item 要压入的元素 * @return bool 如果成功返回true,如果栈已满则返回false */ @@ -152,7 +152,7 @@ class stack { /** * @brief 从栈弹出元素 - * + * * @param item 接收弹出元素的引用 * @return bool 如果成功返回true,如果栈为空则返回false */ @@ -162,9 +162,9 @@ class stack { /** * @brief 检查栈是否为空 - * + * * 注意:在多线程环境中,此操作结果可能立即过期 - * + * * @return bool 如果栈为空返回true */ bool empty() const { @@ -176,4 +176,4 @@ class stack { } // namespace containers } // namespace atom -#endif // defined(ATOM_HAS_BOOST_LOCKFREE) \ No newline at end of file +#endif // defined(ATOM_HAS_BOOST_LOCKFREE) diff --git a/atom/error/CMakeLists.txt b/atom/error/CMakeLists.txt index 3999892b..9f3a86a7 100644 --- a/atom/error/CMakeLists.txt +++ b/atom/error/CMakeLists.txt @@ -50,4 +50,4 @@ set_target_properties(${PROJECT_NAME} PROPERTIES # Install rules install(TARGETS ${PROJECT_NAME} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} -) \ No newline at end of file +) diff --git a/atom/error/stacktrace.cpp b/atom/error/stacktrace.cpp index 996d4faa..41a5b130 100644 --- a/atom/error/stacktrace.cpp +++ b/atom/error/stacktrace.cpp @@ -304,4 +304,4 @@ void StackTrace::capture() { #endif } -} // namespace atom::error \ No newline at end of file +} // namespace atom::error diff --git a/atom/error/stacktrace.hpp b/atom/error/stacktrace.hpp index 0bd3d90c..6beceac7 100644 --- a/atom/error/stacktrace.hpp +++ b/atom/error/stacktrace.hpp @@ -64,4 +64,4 @@ class StackTrace { } // namespace atom::error -#endif \ No newline at end of file +#endif diff --git a/atom/error/xmake.lua b/atom/error/xmake.lua index e82bdc7a..9292f8be 100644 --- a/atom/error/xmake.lua +++ b/atom/error/xmake.lua @@ -38,37 +38,37 @@ local headers = { target("atom-error") -- Set target kind to shared library set_kind("shared") - + -- Add source files add_files(sources) - + -- Add header files add_headerfiles(headers) - + -- Add include directories add_includedirs(".", {public = true}) - + -- Add packages add_packages("loguru") - + -- Add platform-specific libraries if is_plat("linux") then add_syslinks("dl") end - + -- Enable position independent code (automatic for shared libraries) set_policy("build.optimization.lto", true) - + -- Set version info set_version("1.0.0") - + -- Set output name set_basename("atom-error") - + -- Set target and object directories set_targetdir("$(buildir)/lib") set_objectdir("$(buildir)/obj") - + -- Installation rules after_install(function (target) local installdir = target:installdir() or "$(prefix)" @@ -85,20 +85,20 @@ target("atom-error") -- Optional: Create object library target (equivalent to CMake's object library) target("atom-error-object") set_kind("object") - + -- Add the same source files add_files(sources) add_headerfiles(headers) - + -- Configuration add_includedirs(".") add_packages("loguru") - + -- Platform-specific libraries if is_plat("linux") then add_syslinks("dl") end - + -- Enable position independent code add_cxflags("-fPIC", {tools = {"gcc", "clang"}}) add_cflags("-fPIC", {tools = {"gcc", "clang"}}) diff --git a/atom/extra/asio/asio_compatibility.hpp b/atom/extra/asio/asio_compatibility.hpp index 160fafef..dfdb3a56 100644 --- a/atom/extra/asio/asio_compatibility.hpp +++ b/atom/extra/asio/asio_compatibility.hpp @@ -65,4 +65,4 @@ template auto as_tuple_awaitable(AsyncOperation&& op) { return std::forward(op)( net::experimental::as_tuple(use_awaitable)); -} \ No newline at end of file +} diff --git a/atom/extra/asio/mqtt/client.cpp b/atom/extra/asio/mqtt/client.cpp index 3505cb7b..7ebd95d0 100644 --- a/atom/extra/asio/mqtt/client.cpp +++ b/atom/extra/asio/mqtt/client.cpp @@ -781,4 +781,4 @@ void Client::handle_transport_error(ErrorCode error) { } } -} // namespace mqtt \ No newline at end of file +} // namespace mqtt diff --git a/atom/extra/asio/mqtt/client.hpp b/atom/extra/asio/mqtt/client.hpp index 3908b183..58edb6da 100644 --- a/atom/extra/asio/mqtt/client.hpp +++ b/atom/extra/asio/mqtt/client.hpp @@ -557,4 +557,4 @@ class Client { /** @} */ }; -} // namespace mqtt \ No newline at end of file +} // namespace mqtt diff --git a/atom/extra/asio/mqtt/packet.cpp b/atom/extra/asio/mqtt/packet.cpp index 1ce3c6a3..a4e58600 100644 --- a/atom/extra/asio/mqtt/packet.cpp +++ b/atom/extra/asio/mqtt/packet.cpp @@ -417,4 +417,4 @@ Result> PacketCodec::parse_unsuback( return parse_suback(data); // Same format as SUBACK } -} // namespace mqtt \ No newline at end of file +} // namespace mqtt diff --git a/atom/extra/asio/mqtt/packet.hpp b/atom/extra/asio/mqtt/packet.hpp index aa5f4c6a..0b1a64e9 100644 --- a/atom/extra/asio/mqtt/packet.hpp +++ b/atom/extra/asio/mqtt/packet.hpp @@ -407,4 +407,4 @@ class PacketCodec { ProtocolVersion version); }; -} // namespace mqtt \ No newline at end of file +} // namespace mqtt diff --git a/atom/extra/asio/mqtt/protocol.hpp b/atom/extra/asio/mqtt/protocol.hpp index ac9b57d4..9f4da386 100644 --- a/atom/extra/asio/mqtt/protocol.hpp +++ b/atom/extra/asio/mqtt/protocol.hpp @@ -316,4 +316,4 @@ inline bool TLSTransport::is_open() const { return ssl_socket_.lowest_layer().is_open(); } -} // namespace mqtt \ No newline at end of file +} // namespace mqtt diff --git a/atom/extra/asio/mqtt/test_client.hpp b/atom/extra/asio/mqtt/test_client.hpp index d3790a99..ff1981fb 100644 --- a/atom/extra/asio/mqtt/test_client.hpp +++ b/atom/extra/asio/mqtt/test_client.hpp @@ -468,4 +468,4 @@ TEST_F(ClientTest, StatsAfterOperations) { // verifies no crashes auto after_stats = client_->get_stats(); EXPECT_GE(after_stats.messages_sent, initial_stats.messages_sent); -} \ No newline at end of file +} diff --git a/atom/extra/asio/mqtt/test_packet.hpp b/atom/extra/asio/mqtt/test_packet.hpp index 3cd035f8..fd2971d5 100644 --- a/atom/extra/asio/mqtt/test_packet.hpp +++ b/atom/extra/asio/mqtt/test_packet.hpp @@ -249,4 +249,4 @@ TEST(BinaryBufferTest, ReadMalformedPacket) { auto result = buf.read(); EXPECT_FALSE(result.has_value()); EXPECT_EQ(result.error(), ErrorCode::MALFORMED_PACKET); -} \ No newline at end of file +} diff --git a/atom/extra/asio/mqtt/test_protocol.hpp b/atom/extra/asio/mqtt/test_protocol.hpp index 7db44066..f6fd4485 100644 --- a/atom/extra/asio/mqtt/test_protocol.hpp +++ b/atom/extra/asio/mqtt/test_protocol.hpp @@ -195,4 +195,4 @@ TEST(TLSTransportTest, AsyncWriteAndReadError) { transport.close(); EXPECT_FALSE(transport.is_open()); -} \ No newline at end of file +} diff --git a/atom/extra/asio/mqtt/test_types.hpp b/atom/extra/asio/mqtt/test_types.hpp index cd0097cf..b2391ba9 100644 --- a/atom/extra/asio/mqtt/test_types.hpp +++ b/atom/extra/asio/mqtt/test_types.hpp @@ -225,4 +225,4 @@ TEST(CallbackTypesTest, DisconnectionHandler) { }; handler(ErrorCode::SERVER_UNAVAILABLE); EXPECT_TRUE(called); -} \ No newline at end of file +} diff --git a/atom/extra/asio/mqtt/types.hpp b/atom/extra/asio/mqtt/types.hpp index e62eafa4..aa4011a8 100644 --- a/atom/extra/asio/mqtt/types.hpp +++ b/atom/extra/asio/mqtt/types.hpp @@ -174,4 +174,4 @@ using ConnectionHandler = std::function; */ using DisconnectionHandler = std::function; -} // namespace mqtt \ No newline at end of file +} // namespace mqtt diff --git a/atom/extra/asio/sse/client/client.cpp b/atom/extra/asio/sse/client/client.cpp index 6d2f5719..96a7ce45 100644 --- a/atom/extra/asio/sse/client/client.cpp +++ b/atom/extra/asio/sse/client/client.cpp @@ -435,4 +435,4 @@ bool Client::is_connected() const { return pimpl_->is_connected(); } const ClientConfig& Client::config() const { return pimpl_->config(); } -} // namespace atom::extra::asio::sse \ No newline at end of file +} // namespace atom::extra::asio::sse diff --git a/atom/extra/asio/sse/client/client.hpp b/atom/extra/asio/sse/client/client.hpp index c4804e1f..de8533e8 100644 --- a/atom/extra/asio/sse/client/client.hpp +++ b/atom/extra/asio/sse/client/client.hpp @@ -116,4 +116,4 @@ class Client { std::unique_ptr pimpl_; ///< Pointer to implementation. }; -} // namespace atom::extra::asio::sse \ No newline at end of file +} // namespace atom::extra::asio::sse diff --git a/atom/extra/asio/sse/client/client_config.cpp b/atom/extra/asio/sse/client/client_config.cpp index f74e1f54..e9507ecf 100644 --- a/atom/extra/asio/sse/client/client_config.cpp +++ b/atom/extra/asio/sse/client/client_config.cpp @@ -102,4 +102,4 @@ void ClientConfig::save_to_file(const std::string& filename) const { } } -} // namespace sse \ No newline at end of file +} // namespace sse diff --git a/atom/extra/asio/sse/client/client_config.hpp b/atom/extra/asio/sse/client/client_config.hpp index c1fcfe8f..59ad2ac6 100644 --- a/atom/extra/asio/sse/client/client_config.hpp +++ b/atom/extra/asio/sse/client/client_config.hpp @@ -68,4 +68,4 @@ struct ClientConfig { void save_to_file(const std::string& filename) const; }; -} // namespace atom::extra::asio::sse \ No newline at end of file +} // namespace atom::extra::asio::sse diff --git a/atom/extra/asio/sse/event.cpp b/atom/extra/asio/sse/event.cpp index e4de5fb2..9f7ee13a 100644 --- a/atom/extra/asio/sse/event.cpp +++ b/atom/extra/asio/sse/event.cpp @@ -308,4 +308,4 @@ HeartbeatEvent::HeartbeatEvent() .count()), std::string("heartbeat"), std::string("ping")) {} -} // namespace atom::extra::asio::sse \ No newline at end of file +} // namespace atom::extra::asio::sse diff --git a/atom/extra/asio/sse/event.hpp b/atom/extra/asio/sse/event.hpp index a91fc478..e164fcb1 100644 --- a/atom/extra/asio/sse/event.hpp +++ b/atom/extra/asio/sse/event.hpp @@ -250,4 +250,4 @@ class HeartbeatEvent final : public Event { } // namespace atom::extra::asio::sse -#endif // ATOM_EXTRA_ASIO_SSE_EVENT_HPP \ No newline at end of file +#endif // ATOM_EXTRA_ASIO_SSE_EVENT_HPP diff --git a/atom/extra/asio/sse/event_store.cpp b/atom/extra/asio/sse/event_store.cpp index 6fef1be8..560ab912 100644 --- a/atom/extra/asio/sse/event_store.cpp +++ b/atom/extra/asio/sse/event_store.cpp @@ -115,4 +115,4 @@ void EventStore::load_existing_events() { } } -} // namespace atom::extra::asio::sse \ No newline at end of file +} // namespace atom::extra::asio::sse diff --git a/atom/extra/asio/sse/event_store.hpp b/atom/extra/asio/sse/event_store.hpp index d3fbdb8e..a47a4d22 100644 --- a/atom/extra/asio/sse/event_store.hpp +++ b/atom/extra/asio/sse/event_store.hpp @@ -83,4 +83,4 @@ class EventStore { } // namespace atom::extra::asio::sse -#endif // ATOM_EXTRA_ASIO_SSE_EVENT_STORE_HPP \ No newline at end of file +#endif // ATOM_EXTRA_ASIO_SSE_EVENT_STORE_HPP diff --git a/atom/extra/asio/sse/server/auth_service.cpp b/atom/extra/asio/sse/server/auth_service.cpp index b55fe971..974a968a 100644 --- a/atom/extra/asio/sse/server/auth_service.cpp +++ b/atom/extra/asio/sse/server/auth_service.cpp @@ -95,4 +95,4 @@ void AuthService::save_auth_data() { } } -} // namespace atom::extra::asio::sse \ No newline at end of file +} // namespace atom::extra::asio::sse diff --git a/atom/extra/asio/sse/server/auth_service.hpp b/atom/extra/asio/sse/server/auth_service.hpp index 61bf268e..f10605d0 100644 --- a/atom/extra/asio/sse/server/auth_service.hpp +++ b/atom/extra/asio/sse/server/auth_service.hpp @@ -105,4 +105,4 @@ class AuthService { void save_auth_data(); }; -} // namespace atom::extra::asio::sse \ No newline at end of file +} // namespace atom::extra::asio::sse diff --git a/atom/extra/asio/sse/server/connection.cpp b/atom/extra/asio/sse/server/connection.cpp index f58119c9..4391650e 100644 --- a/atom/extra/asio/sse/server/connection.cpp +++ b/atom/extra/asio/sse/server/connection.cpp @@ -470,4 +470,4 @@ net::awaitable SSEConnection::send_event(const Event& event) { client_id_); } -} // namespace atom::extra::asio::sse \ No newline at end of file +} // namespace atom::extra::asio::sse diff --git a/atom/extra/asio/sse/server/connection.hpp b/atom/extra/asio/sse/server/connection.hpp index 0602c832..bba02f15 100644 --- a/atom/extra/asio/sse/server/connection.hpp +++ b/atom/extra/asio/sse/server/connection.hpp @@ -91,4 +91,4 @@ class SSEConnection : public std::enable_shared_from_this { bool authenticate_client(const HttpRequest& request); }; -} // namespace atom::extra::asio::sse \ No newline at end of file +} // namespace atom::extra::asio::sse diff --git a/atom/extra/asio/sse/server/event_queue.cpp b/atom/extra/asio/sse/server/event_queue.cpp index 0c1ce2e1..d87f17ff 100644 --- a/atom/extra/asio/sse/server/event_queue.cpp +++ b/atom/extra/asio/sse/server/event_queue.cpp @@ -30,4 +30,4 @@ std::optional EventQueue::pop_event() { return event; } -} // namespace atom::extra::asio::sse \ No newline at end of file +} // namespace atom::extra::asio::sse diff --git a/atom/extra/asio/sse/server/event_queue.hpp b/atom/extra/asio/sse/server/event_queue.hpp index 8bcd4b9e..6cdc0611 100644 --- a/atom/extra/asio/sse/server/event_queue.hpp +++ b/atom/extra/asio/sse/server/event_queue.hpp @@ -33,4 +33,4 @@ class EventQueue { bool persist_events_; }; -} // namespace sse_server \ No newline at end of file +} // namespace sse_server diff --git a/atom/extra/asio/sse/server/event_store.cpp b/atom/extra/asio/sse/server/event_store.cpp index 2911d582..0a5d7ec3 100644 --- a/atom/extra/asio/sse/server/event_store.cpp +++ b/atom/extra/asio/sse/server/event_store.cpp @@ -147,4 +147,4 @@ void EventStore::persist_event(const Event& event) { } } -} // namespace atom::extra::asio::sse \ No newline at end of file +} // namespace atom::extra::asio::sse diff --git a/atom/extra/asio/sse/server/event_store.hpp b/atom/extra/asio/sse/server/event_store.hpp index ad65fb68..c93b97b7 100644 --- a/atom/extra/asio/sse/server/event_store.hpp +++ b/atom/extra/asio/sse/server/event_store.hpp @@ -117,4 +117,4 @@ class EventStore { void persist_event(const Event& event); }; -} // namespace atom::extra::asio::sse \ No newline at end of file +} // namespace atom::extra::asio::sse diff --git a/atom/extra/asio/sse/server/http_request.cpp b/atom/extra/asio/sse/server/http_request.cpp index 6aa9de53..8a4589cb 100644 --- a/atom/extra/asio/sse/server/http_request.cpp +++ b/atom/extra/asio/sse/server/http_request.cpp @@ -51,4 +51,4 @@ std::optional HttpRequest::get_last_event_id() const { return std::nullopt; } -} // namespace atom::extra::asio::sse \ No newline at end of file +} // namespace atom::extra::asio::sse diff --git a/atom/extra/asio/sse/server/http_request.hpp b/atom/extra/asio/sse/server/http_request.hpp index 8274e9cc..3ab2ef33 100644 --- a/atom/extra/asio/sse/server/http_request.hpp +++ b/atom/extra/asio/sse/server/http_request.hpp @@ -79,4 +79,4 @@ struct HttpRequest { std::optional get_last_event_id() const; }; -} // namespace atom::extra::asio::sse \ No newline at end of file +} // namespace atom::extra::asio::sse diff --git a/atom/extra/asio/sse/server/metrics.cpp b/atom/extra/asio/sse/server/metrics.cpp index d2f482eb..f76ca897 100644 --- a/atom/extra/asio/sse/server/metrics.cpp +++ b/atom/extra/asio/sse/server/metrics.cpp @@ -50,4 +50,4 @@ void ServerMetrics::update_max_concurrent() { } } -} // namespace atom::extra::asio::sse \ No newline at end of file +} // namespace atom::extra::asio::sse diff --git a/atom/extra/asio/sse/server/metrics.hpp b/atom/extra/asio/sse/server/metrics.hpp index 82b97883..818335a4 100644 --- a/atom/extra/asio/sse/server/metrics.hpp +++ b/atom/extra/asio/sse/server/metrics.hpp @@ -125,4 +125,4 @@ class ServerMetrics { void update_max_concurrent(); }; -} // namespace atom::extra::asio::sse \ No newline at end of file +} // namespace atom::extra::asio::sse diff --git a/atom/extra/asio/sse/server/server.cpp b/atom/extra/asio/sse/server/server.cpp index 4ac88947..47698e22 100644 --- a/atom/extra/asio/sse/server/server.cpp +++ b/atom/extra/asio/sse/server/server.cpp @@ -178,4 +178,4 @@ std::string generate_id() { return std::to_string(counter++); } -} // namespace atom::extra::asio::sse \ No newline at end of file +} // namespace atom::extra::asio::sse diff --git a/atom/extra/asio/sse/server/server.hpp b/atom/extra/asio/sse/server/server.hpp index fdb23a14..23ac5644 100644 --- a/atom/extra/asio/sse/server/server.hpp +++ b/atom/extra/asio/sse/server/server.hpp @@ -182,4 +182,4 @@ class SSEServer { */ std::string generate_id(); -} // namespace atom::extra::asio::sse \ No newline at end of file +} // namespace atom::extra::asio::sse diff --git a/atom/extra/asio/sse/server/server_config.cpp b/atom/extra/asio/sse/server/server_config.cpp index 4357018a..61665594 100644 --- a/atom/extra/asio/sse/server/server_config.cpp +++ b/atom/extra/asio/sse/server/server_config.cpp @@ -69,4 +69,4 @@ void ServerConfig::save_to_file(const std::string& filename) const { } } -} // namespace atom::extra::asio::sse \ No newline at end of file +} // namespace atom::extra::asio::sse diff --git a/atom/extra/asio/sse/server/server_config.hpp b/atom/extra/asio/sse/server/server_config.hpp index c38aa984..7148c8cc 100644 --- a/atom/extra/asio/sse/server/server_config.hpp +++ b/atom/extra/asio/sse/server/server_config.hpp @@ -125,4 +125,4 @@ struct ServerConfig { void save_to_file(const std::string& filename) const; }; -} // namespace atom::extra::asio::sse \ No newline at end of file +} // namespace atom::extra::asio::sse diff --git a/atom/extra/asio/sse/sse.hpp b/atom/extra/asio/sse/sse.hpp index 2fed7d6f..0868efe0 100644 --- a/atom/extra/asio/sse/sse.hpp +++ b/atom/extra/asio/sse/sse.hpp @@ -10,4 +10,4 @@ #include "client_config.hpp" #include "event_store.hpp" #include "client.hpp" -#include "logger.hpp" \ No newline at end of file +#include "logger.hpp" diff --git a/atom/extra/beast/ws.cpp b/atom/extra/beast/ws.cpp index fc094f4c..435b78b1 100644 --- a/atom/extra/beast/ws.cpp +++ b/atom/extra/beast/ws.cpp @@ -269,4 +269,4 @@ void WSClient::startPing() { } })); })); -} \ No newline at end of file +} diff --git a/atom/extra/beast/ws.hpp b/atom/extra/beast/ws.hpp index caf335a8..d3fa77c1 100644 --- a/atom/extra/beast/ws.hpp +++ b/atom/extra/beast/ws.hpp @@ -436,4 +436,4 @@ void WSClient::handleConnectError(beast::error_code ec, } } -#endif // ATOM_EXTRA_BEAST_WS_HPP \ No newline at end of file +#endif // ATOM_EXTRA_BEAST_WS_HPP diff --git a/atom/extra/curl/cache.hpp b/atom/extra/curl/cache.hpp index efd7ee64..ee476344 100644 --- a/atom/extra/curl/cache.hpp +++ b/atom/extra/curl/cache.hpp @@ -111,4 +111,4 @@ class Cache { }; } // namespace atom::extra::curl -#endif // ATOM_EXTRA_CURL_CACHE_HPP \ No newline at end of file +#endif // ATOM_EXTRA_CURL_CACHE_HPP diff --git a/atom/extra/curl/connection_pool.hpp b/atom/extra/curl/connection_pool.hpp index 16e473cf..a0971658 100644 --- a/atom/extra/curl/connection_pool.hpp +++ b/atom/extra/curl/connection_pool.hpp @@ -20,4 +20,4 @@ class ConnectionPool { }; } // namespace atom::extra::curl -#endif \ No newline at end of file +#endif diff --git a/atom/extra/curl/cookie.hpp b/atom/extra/curl/cookie.hpp index 576e6cb1..0796d8ed 100644 --- a/atom/extra/curl/cookie.hpp +++ b/atom/extra/curl/cookie.hpp @@ -208,4 +208,4 @@ class CookieJar { }; } // namespace atom::extra::curl -#endif // ATOM_EXTRA_CURL_COOKIE_HPP \ No newline at end of file +#endif // ATOM_EXTRA_CURL_COOKIE_HPP diff --git a/atom/extra/curl/error.cpp b/atom/extra/curl/error.cpp index c519245b..97ac9c07 100644 --- a/atom/extra/curl/error.cpp +++ b/atom/extra/curl/error.cpp @@ -16,4 +16,4 @@ std::optional Error::multi_code() const noexcept { return multi_code_; } -} // namespace atom::extra::curl \ No newline at end of file +} // namespace atom::extra::curl diff --git a/atom/extra/curl/error.hpp b/atom/extra/curl/error.hpp index 588f69e6..fb0ef777 100644 --- a/atom/extra/curl/error.hpp +++ b/atom/extra/curl/error.hpp @@ -55,4 +55,4 @@ class Error : public std::runtime_error { } // namespace atom::extra::curl -#endif // ATOM_EXTRA_CURL_ERROR_HPP \ No newline at end of file +#endif // ATOM_EXTRA_CURL_ERROR_HPP diff --git a/atom/extra/curl/interceptor.hpp b/atom/extra/curl/interceptor.hpp index 685ad02b..e28d6f34 100644 --- a/atom/extra/curl/interceptor.hpp +++ b/atom/extra/curl/interceptor.hpp @@ -60,4 +60,4 @@ class Interceptor { }; } // namespace atom::extra::curl -#endif // ATOM_EXTRA_CURL_INTERCEPTOR_HPP \ No newline at end of file +#endif // ATOM_EXTRA_CURL_INTERCEPTOR_HPP diff --git a/atom/extra/curl/multi_session.cpp b/atom/extra/curl/multi_session.cpp index b61aa69e..a7ab72b8 100644 --- a/atom/extra/curl/multi_session.cpp +++ b/atom/extra/curl/multi_session.cpp @@ -268,4 +268,4 @@ size_t MultiSession::header_callback(char* buffer, size_t size, size_t nitems, return realsize; } -} // namespace atom::extra::curl \ No newline at end of file +} // namespace atom::extra::curl diff --git a/atom/extra/curl/multi_session.hpp b/atom/extra/curl/multi_session.hpp index 127149bf..786fe66e 100644 --- a/atom/extra/curl/multi_session.hpp +++ b/atom/extra/curl/multi_session.hpp @@ -131,4 +131,4 @@ class MultiSession { }; } // namespace atom::extra::curl -#endif // ATOM_EXTRA_CURL_MULTI_SESSION_HPP \ No newline at end of file +#endif // ATOM_EXTRA_CURL_MULTI_SESSION_HPP diff --git a/atom/extra/curl/multipart.cpp b/atom/extra/curl/multipart.cpp index dde3d3ef..444c34e1 100644 --- a/atom/extra/curl/multipart.cpp +++ b/atom/extra/curl/multipart.cpp @@ -87,4 +87,4 @@ void MultipartForm::initialize() { form_ = curl_mime_init(curl); curl_easy_cleanup(curl); } -} // namespace atom::extra::curl \ No newline at end of file +} // namespace atom::extra::curl diff --git a/atom/extra/curl/multipart.hpp b/atom/extra/curl/multipart.hpp index 8a65e01b..2d23dbb9 100644 --- a/atom/extra/curl/multipart.hpp +++ b/atom/extra/curl/multipart.hpp @@ -115,4 +115,4 @@ class MultipartForm { }; } // namespace atom::extra::curl -#endif // ATOM_EXTRA_CURL_MULTIPART_HPP \ No newline at end of file +#endif // ATOM_EXTRA_CURL_MULTIPART_HPP diff --git a/atom/extra/curl/rate_limiter.hpp b/atom/extra/curl/rate_limiter.hpp index 1798bfef..51595165 100644 --- a/atom/extra/curl/rate_limiter.hpp +++ b/atom/extra/curl/rate_limiter.hpp @@ -51,4 +51,4 @@ class RateLimiter { } // namespace atom::extra::curl -#endif // ATOM_EXTRA_CURL_RATE_LIMITER_HPP \ No newline at end of file +#endif // ATOM_EXTRA_CURL_RATE_LIMITER_HPP diff --git a/atom/extra/curl/request.hpp b/atom/extra/curl/request.hpp index 8df00706..3fb6c08f 100644 --- a/atom/extra/curl/request.hpp +++ b/atom/extra/curl/request.hpp @@ -631,4 +631,4 @@ class Request { }; } // namespace atom::extra::curl -#endif // ATOM_EXTRA_CURL_REQUEST_HPP \ No newline at end of file +#endif // ATOM_EXTRA_CURL_REQUEST_HPP diff --git a/atom/extra/curl/response.hpp b/atom/extra/curl/response.hpp index 3a8a1701..7268bf71 100644 --- a/atom/extra/curl/response.hpp +++ b/atom/extra/curl/response.hpp @@ -139,4 +139,4 @@ class Response { }; } // namespace atom::extra::curl -#endif // ATOM_EXTRA_CURL_RESPONSE_HPP \ No newline at end of file +#endif // ATOM_EXTRA_CURL_RESPONSE_HPP diff --git a/atom/extra/curl/rest_client.hpp b/atom/extra/curl/rest_client.hpp index acd1f53e..71a3db3d 100644 --- a/atom/extra/curl/rest_client.hpp +++ b/atom/extra/curl/rest_client.hpp @@ -509,4 +509,4 @@ class RestClient { }; } // namespace atom::extra::curl -#endif // ATOM_EXTRA_CURL_REST_CLIENT_HPP \ No newline at end of file +#endif // ATOM_EXTRA_CURL_REST_CLIENT_HPP diff --git a/atom/extra/curl/session.hpp b/atom/extra/curl/session.hpp index 0f5fa0eb..00a73745 100644 --- a/atom/extra/curl/session.hpp +++ b/atom/extra/curl/session.hpp @@ -378,4 +378,4 @@ class Session { }; } // namespace atom::extra::curl -#endif // ATOM_EXTRA_CURL_SESSION_HPP \ No newline at end of file +#endif // ATOM_EXTRA_CURL_SESSION_HPP diff --git a/atom/extra/curl/session_pool.cpp b/atom/extra/curl/session_pool.cpp index dab709f7..59efee70 100644 --- a/atom/extra/curl/session_pool.cpp +++ b/atom/extra/curl/session_pool.cpp @@ -33,4 +33,4 @@ void SessionPool::release(std::shared_ptr session) { } // 如果池已满,session 会自动析构 } -} // namespace atom::extra::curl \ No newline at end of file +} // namespace atom::extra::curl diff --git a/atom/extra/curl/session_pool.hpp b/atom/extra/curl/session_pool.hpp index 01747940..3b2e5243 100644 --- a/atom/extra/curl/session_pool.hpp +++ b/atom/extra/curl/session_pool.hpp @@ -66,4 +66,4 @@ class SessionPool { }; } // namespace atom::extra::curl -#endif // ATOM_EXTRA_CURL_SESSION_POOL_HPP \ No newline at end of file +#endif // ATOM_EXTRA_CURL_SESSION_POOL_HPP diff --git a/atom/extra/curl/websocket.hpp b/atom/extra/curl/websocket.hpp index 581498f4..ddb3be7e 100644 --- a/atom/extra/curl/websocket.hpp +++ b/atom/extra/curl/websocket.hpp @@ -166,4 +166,4 @@ class WebSocket { }; } // namespace atom::extra::curl -#endif // ATOM_EXTRA_CURL_WEBSOCKET_HPP \ No newline at end of file +#endif // ATOM_EXTRA_CURL_WEBSOCKET_HPP diff --git a/atom/extra/dotenv/CMakeLists.txt b/atom/extra/dotenv/CMakeLists.txt index ba220d02..cecc7c9f 100644 --- a/atom/extra/dotenv/CMakeLists.txt +++ b/atom/extra/dotenv/CMakeLists.txt @@ -72,4 +72,4 @@ install(FILES ${HEADERS} DESTINATION include/dotenv) install(EXPORT dotenv-cpp-targets FILE dotenv-cpp-config.cmake DESTINATION lib/cmake/dotenv-cpp -) \ No newline at end of file +) diff --git a/atom/extra/dotenv/dotenv.cpp b/atom/extra/dotenv/dotenv.cpp index 4e64d9e1..735528d7 100644 --- a/atom/extra/dotenv/dotenv.cpp +++ b/atom/extra/dotenv/dotenv.cpp @@ -272,4 +272,4 @@ void Dotenv::config(const std::filesystem::path& filepath, } } -} // namespace dotenv \ No newline at end of file +} // namespace dotenv diff --git a/atom/extra/dotenv/dotenv.hpp b/atom/extra/dotenv/dotenv.hpp index 4a489da2..c145b328 100644 --- a/atom/extra/dotenv/dotenv.hpp +++ b/atom/extra/dotenv/dotenv.hpp @@ -258,4 +258,4 @@ class Dotenv { const std::vector& source_files = {}); }; -} // namespace dotenv \ No newline at end of file +} // namespace dotenv diff --git a/atom/extra/dotenv/exceptions.hpp b/atom/extra/dotenv/exceptions.hpp index 22b88589..6fe41867 100644 --- a/atom/extra/dotenv/exceptions.hpp +++ b/atom/extra/dotenv/exceptions.hpp @@ -42,4 +42,4 @@ class ValidationException : public DotenvException { : DotenvException("Validation Error: " + message) {} }; -} // namespace dotenv \ No newline at end of file +} // namespace dotenv diff --git a/atom/extra/dotenv/loader.cpp b/atom/extra/dotenv/loader.cpp index a1d3ae3b..62bd4cc3 100644 --- a/atom/extra/dotenv/loader.cpp +++ b/atom/extra/dotenv/loader.cpp @@ -268,4 +268,4 @@ std::string FileLoader::convertEncoding(const std::string& content, return content; } -} // namespace dotenv \ No newline at end of file +} // namespace dotenv diff --git a/atom/extra/dotenv/loader.hpp b/atom/extra/dotenv/loader.hpp index 86d0a06a..de0e9ef0 100644 --- a/atom/extra/dotenv/loader.hpp +++ b/atom/extra/dotenv/loader.hpp @@ -158,4 +158,4 @@ class FileLoader { const std::string& from_encoding); }; -} // namespace dotenv \ No newline at end of file +} // namespace dotenv diff --git a/atom/extra/dotenv/parser.cpp b/atom/extra/dotenv/parser.cpp index 87767b09..2e448543 100644 --- a/atom/extra/dotenv/parser.cpp +++ b/atom/extra/dotenv/parser.cpp @@ -235,4 +235,4 @@ void Parser::setVariableExpander(VariableExpander expander) { variable_expander_ = std::move(expander); } -} // namespace dotenv \ No newline at end of file +} // namespace dotenv diff --git a/atom/extra/dotenv/parser.hpp b/atom/extra/dotenv/parser.hpp index afc7a4a6..d730dd5f 100644 --- a/atom/extra/dotenv/parser.hpp +++ b/atom/extra/dotenv/parser.hpp @@ -82,4 +82,4 @@ class Parser { bool isEmpty(const std::string& line); }; -} // namespace dotenv \ No newline at end of file +} // namespace dotenv diff --git a/atom/extra/dotenv/test_dotenv.hpp b/atom/extra/dotenv/test_dotenv.hpp index e34a8db7..eda3c2de 100644 --- a/atom/extra/dotenv/test_dotenv.hpp +++ b/atom/extra/dotenv/test_dotenv.hpp @@ -259,4 +259,4 @@ TEST_F(DotenvTest, StaticConfigSuccess) { TEST_F(DotenvTest, StaticConfigFailureThrows) { auto file = dir / "bad.env"; EXPECT_THROW(Dotenv::config(file, true), DotenvException); -} \ No newline at end of file +} diff --git a/atom/extra/dotenv/test_validator.hpp b/atom/extra/dotenv/test_validator.hpp index c2dd990f..e4ee7cff 100644 --- a/atom/extra/dotenv/test_validator.hpp +++ b/atom/extra/dotenv/test_validator.hpp @@ -196,4 +196,4 @@ TEST_F(ValidatorTest, ValidatorValidateNoRules) { auto result = validator.validate(env, schema); EXPECT_TRUE(result.is_valid); EXPECT_TRUE(result.errors.empty()); -} \ No newline at end of file +} diff --git a/atom/extra/dotenv/validator.cpp b/atom/extra/dotenv/validator.cpp index 4671e366..59d5c610 100644 --- a/atom/extra/dotenv/validator.cpp +++ b/atom/extra/dotenv/validator.cpp @@ -225,4 +225,4 @@ std::shared_ptr custom(ValidationRule::Validator validator, } // namespace rules -} // namespace dotenv \ No newline at end of file +} // namespace dotenv diff --git a/atom/extra/dotenv/validator.hpp b/atom/extra/dotenv/validator.hpp index 862f7470..8447f381 100644 --- a/atom/extra/dotenv/validator.hpp +++ b/atom/extra/dotenv/validator.hpp @@ -152,4 +152,4 @@ class Validator { ValidationResult& result); }; -} // namespace dotenv \ No newline at end of file +} // namespace dotenv diff --git a/atom/extra/iconv/test_iconv_cpp.cpp b/atom/extra/iconv/test_iconv_cpp.cpp index b60eb5b9..6dc4f54f 100644 --- a/atom/extra/iconv/test_iconv_cpp.cpp +++ b/atom/extra/iconv/test_iconv_cpp.cpp @@ -21,12 +21,12 @@ class IconvCppTest : public ::testing::Test { temp_input = fs::temp_directory_path() / "iconv_test_input.txt"; temp_output = fs::temp_directory_path() / "iconv_test_output.txt"; temp_output2 = fs::temp_directory_path() / "iconv_test_output2.txt"; - + // Create test file with UTF-8 content including multibyte characters std::ofstream ofs(temp_input, std::ios::binary); ofs << "Hello, 世界! 🌍\nTest file with UTF-8 content.\n"; ofs.close(); - + // Create ASCII test file temp_ascii = fs::temp_directory_path() / "iconv_test_ascii.txt"; std::ofstream ascii_ofs(temp_ascii, std::ios::binary); @@ -60,12 +60,12 @@ TEST_F(IconvCppTest, ConverterMoveSemantics) { Converter conv1("UTF-8", "UTF-8"); std::string test = "move test"; auto result1 = conv1.convert_string(test); - + // Move constructor Converter conv2 = std::move(conv1); auto result2 = conv2.convert_string(test); EXPECT_EQ(result1, result2); - + // Move assignment Converter conv3("UTF-8", "UTF-16LE"); conv3 = std::move(conv2); @@ -84,10 +84,10 @@ TEST_F(IconvCppTest, UTF8ToUTF16RoundTrip) { std::string utf8 = "Hello, 世界! 🌍"; UTF8ToUTF16Converter to16; UTF16ToUTF8Converter to8; - + auto utf16 = to16.convert_u16string(utf8); EXPECT_GT(utf16.size(), 0); - + std::string roundtrip = to8.convert_u16string(utf16); EXPECT_EQ(utf8, roundtrip); } @@ -96,10 +96,10 @@ TEST_F(IconvCppTest, UTF8ToUTF32RoundTrip) { std::string utf8 = "Test 🌍 emoji"; UTF8ToUTF32Converter to32; UTF32ToUTF8Converter to8; - + auto utf32 = to32.convert_u32string(utf8); EXPECT_GT(utf32.size(), 0); - + std::string roundtrip = to8.convert_u32string(utf32); EXPECT_EQ(utf8, roundtrip); } @@ -116,7 +116,7 @@ TEST_F(IconvCppTest, ErrorHandlingReplace) { ConversionOptions opts; opts.error_policy = ErrorHandlingPolicy::Replace; opts.replacement_char = '?'; - + Converter conv("UTF-8", "UTF-8", opts); std::string result = conv.convert_string(invalid_utf8); EXPECT_TRUE(result.find('?') != std::string::npos); @@ -127,7 +127,7 @@ TEST_F(IconvCppTest, ErrorHandlingSkip) { std::string invalid_utf8 = "abc\xFF\\xFEdef"; ConversionOptions opts; opts.error_policy = ErrorHandlingPolicy::Skip; - + Converter conv("UTF-8", "UTF-8", opts); std::string result = conv.convert_string(invalid_utf8); EXPECT_TRUE(result.find("abc") != std::string::npos); @@ -139,7 +139,7 @@ TEST_F(IconvCppTest, ErrorHandlingIgnore) { std::string invalid_utf8 = "abc\xFF\xFE"; ConversionOptions opts; opts.error_policy = ErrorHandlingPolicy::Ignore; - + Converter conv("UTF-8", "UTF-8", opts); std::string result = conv.convert_string(invalid_utf8); EXPECT_TRUE(result.find("abc") != std::string::npos); @@ -175,15 +175,15 @@ TEST_F(IconvCppTest, FileConversion) { TEST_F(IconvCppTest, FileConversionWithProgress) { bool progress_called = false; size_t last_processed = 0; - + auto progress_cb = [&](size_t processed, size_t total) { progress_called = true; EXPECT_LE(processed, total); EXPECT_GE(processed, last_processed); last_processed = processed; }; - - EXPECT_TRUE(convert_file("UTF-8", "UTF-8", temp_input, temp_output, + + EXPECT_TRUE(convert_file("UTF-8", "UTF-8", temp_input, temp_output, ConversionOptions(), progress_cb)); EXPECT_TRUE(progress_called); } @@ -246,7 +246,7 @@ TEST_F(IconvCppTest, BomAddition) { std::vector data = {'H', 'e', 'l', 'l', 'o'}; auto with_bom = BomHandler::add_bom("UTF-8", data); EXPECT_GT(with_bom.size(), data.size()); - + auto [detected_enc, bom_size] = BomHandler::detect_bom(with_bom); EXPECT_EQ(detected_enc, "UTF-8"); EXPECT_EQ(bom_size, 3); @@ -299,7 +299,7 @@ TEST_F(IconvCppTest, EncodingDetectionMaxResults) { TEST_F(IconvCppTest, FileEncodingDetection) { auto encoding = detect_file_encoding(temp_ascii); EXPECT_TRUE(encoding == "ASCII" || encoding == "UTF-8"); - + encoding = detect_file_encoding(temp_input); EXPECT_TRUE(encoding == "UTF-8" || encoding == "ASCII"); } @@ -320,7 +320,7 @@ TEST_F(IconvCppTest, EncodingRegistryListEncodings) { auto encodings = registry.list_all_encodings(); EXPECT_FALSE(encodings.empty()); EXPECT_GT(encodings.size(), 10); - + // Check for common encodings bool found_utf8 = false, found_ascii = false; for (const auto& enc : encodings) { @@ -346,7 +346,7 @@ TEST_F(IconvCppTest, EncodingRegistryInfo) { EXPECT_TRUE(info->is_ascii_compatible); EXPECT_EQ(info->min_char_size, 1); EXPECT_EQ(info->max_char_size, 4); - + auto invalid_info = registry.get_encoding_info("INVALID-ENCODING"); EXPECT_FALSE(invalid_info.has_value()); } @@ -355,7 +355,7 @@ TEST_F(IconvCppTest, EncodingRegistryInfo) { TEST_F(IconvCppTest, BufferManagerCreate) { auto buffer = BufferManager::create_resizable_buffer(1024); EXPECT_EQ(buffer.size(), 1024); - + auto default_buffer = BufferManager::create_resizable_buffer(); EXPECT_EQ(default_buffer.size(), 4096); } @@ -363,7 +363,7 @@ TEST_F(IconvCppTest, BufferManagerCreate) { TEST_F(IconvCppTest, BufferManagerEnsureCapacity) { auto buffer = BufferManager::create_resizable_buffer(10); EXPECT_EQ(buffer.size(), 10); - + BufferManager::ensure_buffer_capacity(buffer, 50); EXPECT_GE(buffer.size(), 50); } @@ -371,7 +371,7 @@ TEST_F(IconvCppTest, BufferManagerEnsureCapacity) { TEST_F(IconvCppTest, BufferManagerEstimateSize) { size_t estimate = BufferManager::estimate_output_size(100, "UTF-8", "UTF-16LE"); EXPECT_GT(estimate, 100); - + size_t unknown_estimate = BufferManager::estimate_output_size(100, "UNKNOWN", "UNKNOWN"); EXPECT_EQ(unknown_estimate, 400); // 4x fallback } @@ -381,16 +381,16 @@ TEST_F(IconvCppTest, ProgressCallbackCalled) { std::string large_input(10000, 'a'); bool callback_called = false; size_t max_processed = 0; - + auto progress_cb = [&](size_t processed, size_t total) { callback_called = true; EXPECT_LE(processed, total); max_processed = std::max(max_processed, processed); }; - + Converter conv("UTF-8", "UTF-8"); auto result = conv.convert_with_progress({large_input.data(), large_input.size()}, progress_cb); - + EXPECT_TRUE(callback_called); EXPECT_EQ(max_processed, large_input.size()); EXPECT_EQ(result.size(), large_input.size()); @@ -400,17 +400,17 @@ TEST_F(IconvCppTest, ProgressCallbackCalled) { TEST_F(IconvCppTest, StatefulConversion) { ConversionState state; Converter conv("UTF-8", "UTF-8"); - + std::string part1 = "First part "; std::string part2 = "Second part"; - + auto out1 = conv.convert_with_state({part1.data(), part1.size()}, state); EXPECT_GT(state.processed_input_bytes, 0); EXPECT_GT(state.processed_output_bytes, 0); - + auto out2 = conv.convert_with_state({part2.data(), part2.size()}, state); EXPECT_EQ(state.processed_input_bytes, part1.size() + part2.size()); - + std::string combined(out1.begin(), out1.end()); combined.append(out2.begin(), out2.end()); EXPECT_EQ(combined, part1 + part2); @@ -422,7 +422,7 @@ TEST_F(IconvCppTest, ConversionStateReset) { state.processed_output_bytes = 50; state.is_complete = true; state.state_data = {'a', 'b', 'c'}; - + state.reset(); EXPECT_EQ(state.processed_input_bytes, 0); EXPECT_EQ(state.processed_output_bytes, 0); @@ -435,30 +435,30 @@ TEST_F(IconvCppTest, StreamConverter) { std::string input = "Stream conversion test with 中文"; std::istringstream iss(input); std::ostringstream oss; - + StreamConverter sc("UTF-8", "UTF-8"); sc.convert(iss, oss); - + EXPECT_EQ(oss.str(), input); } TEST_F(IconvCppTest, StreamConverterToString) { std::string input = "Convert to string test"; std::istringstream iss(input); - + StreamConverter sc("UTF-8", "UTF-8"); std::string result = sc.convert_to_string(iss); - + EXPECT_EQ(result, input); } TEST_F(IconvCppTest, StreamConverterFromString) { std::string input = "Convert from string test"; std::ostringstream oss; - + StreamConverter sc("UTF-8", "UTF-8"); sc.convert_from_string(input, oss); - + EXPECT_EQ(oss.str(), input); } @@ -467,15 +467,15 @@ TEST_F(IconvCppTest, StreamConverterWithProgress) { std::istringstream iss(input); std::ostringstream oss; bool progress_called = false; - + auto progress_cb = [&](size_t processed, size_t total) { progress_called = true; EXPECT_LE(processed, total); }; - + StreamConverter sc("UTF-8", "UTF-8"); sc.convert(iss, oss, progress_cb); - + EXPECT_EQ(oss.str(), input); // Note: Progress may not be called for small inputs } @@ -484,7 +484,7 @@ TEST_F(IconvCppTest, StreamConverterWithProgress) { TEST_F(IconvCppTest, BatchConverterStrings) { BatchConverter batch("UTF-8", "UTF-8"); std::vector inputs = {"first", "second", "third 中文"}; - + auto outputs = batch.convert_strings(inputs); EXPECT_EQ(outputs.size(), inputs.size()); EXPECT_EQ(outputs, inputs); @@ -494,7 +494,7 @@ TEST_F(IconvCppTest, BatchConverterFiles) { BatchConverter batch("UTF-8", "UTF-8"); std::vector input_paths = {temp_input}; std::vector output_paths = {temp_output}; - + auto results = batch.convert_files(input_paths, output_paths); EXPECT_EQ(results.size(), 1); EXPECT_TRUE(results[0]); @@ -505,7 +505,7 @@ TEST_F(IconvCppTest, BatchConverterFilesMismatch) { BatchConverter batch("UTF-8", "UTF-8"); std::vector input_paths = {temp_input, temp_ascii}; std::vector output_paths = {temp_output}; // Size mismatch - + EXPECT_THROW(batch.convert_files(input_paths, output_paths), IconvError); } @@ -513,7 +513,7 @@ TEST_F(IconvCppTest, BatchConverterParallel) { BatchConverter batch("UTF-8", "UTF-8"); std::vector input_paths = {temp_input, temp_ascii}; std::vector output_paths = {temp_output, temp_output2}; - + auto results = batch.convert_files_parallel(input_paths, output_paths, 2); EXPECT_EQ(results.size(), 2); EXPECT_TRUE(results[0]); @@ -526,19 +526,19 @@ TEST_F(IconvCppTest, BatchConverterParallel) { TEST_F(IconvCppTest, ChineseEncodingConverter) { ChineseEncodingConverter conv; std::string utf8 = "你好世界"; - + // Test GB18030 conversion std::string gb18030 = conv.utf8_to_gb18030_string(utf8); EXPECT_NE(gb18030, utf8); std::string utf8_back = conv.gb18030_to_utf8_string(gb18030); EXPECT_EQ(utf8_back, utf8); - + // Test GBK conversion std::string gbk = conv.utf8_to_gbk_string(utf8); EXPECT_NE(gbk, utf8); utf8_back = conv.gbk_to_utf8_string(gbk); EXPECT_EQ(utf8_back, utf8); - + // Test Big5 conversion std::string big5 = conv.utf8_to_big5_string(utf8); EXPECT_NE(big5, utf8); @@ -549,13 +549,13 @@ TEST_F(IconvCppTest, ChineseEncodingConverter) { TEST_F(IconvCppTest, JapaneseEncodingConverter) { JapaneseEncodingConverter conv; std::string utf8 = "こんにちは"; - + // Test Shift-JIS conversion std::string sjis = conv.utf8_to_shift_jis_string(utf8); EXPECT_NE(sjis, utf8); std::string utf8_back = conv.shift_jis_to_utf8_string(sjis); EXPECT_EQ(utf8_back, utf8); - + // Test EUC-JP conversion std::string euc_jp = conv.utf8_to_euc_jp_string(utf8); EXPECT_NE(euc_jp, utf8); @@ -566,7 +566,7 @@ TEST_F(IconvCppTest, JapaneseEncodingConverter) { TEST_F(IconvCppTest, KoreanEncodingConverter) { KoreanEncodingConverter conv; std::string utf8 = "안녕하세요"; - + // Test EUC-KR conversion std::string euc_kr = conv.utf8_to_euc_kr_string(utf8); EXPECT_NE(euc_kr, utf8); @@ -592,12 +592,12 @@ TEST_F(IconvCppTest, ConvertFunction) { TEST_F(IconvCppTest, ThreadSafety) { std::string input = "Thread safety test 线程安全测试"; Converter conv("UTF-8", "UTF-8"); - + const int num_threads = 4; const int iterations = 100; std::vector threads; std::vector results(num_threads, true); - + for (int t = 0; t < num_threads; ++t) { threads.emplace_back([&conv, &input, &results, t, iterations]() { try { @@ -613,11 +613,11 @@ TEST_F(IconvCppTest, ThreadSafety) { } }); } - + for (auto& thread : threads) { thread.join(); } - + for (bool result : results) { EXPECT_TRUE(result); } @@ -633,7 +633,7 @@ TEST_F(IconvCppTest, ConverterReset) { Converter conv("UTF-8", "UTF-8"); std::string test = "Reset test"; auto result1 = conv.convert_string(test); - + conv.reset(); // Should not affect subsequent conversions auto result2 = conv.convert_string(test); EXPECT_EQ(result1, result2); @@ -643,15 +643,15 @@ TEST_F(IconvCppTest, ConverterReset) { TEST_F(IconvCppTest, LargeInputPerformance) { const size_t large_size = 1024 * 1024; // 1MB std::string large_input(large_size, 'A'); - + auto start = std::chrono::high_resolution_clock::now(); - + Converter conv("UTF-8", "UTF-8"); auto result = conv.convert_string(large_input); - + auto end = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast(end - start); - + EXPECT_EQ(result.size(), large_size); // Performance assertion - should complete within reasonable time EXPECT_LT(duration.count(), 1000); // Less than 1 second @@ -692,4 +692,4 @@ TEST_F(IconvCppTest, MixedContentConversion) { std::string mixed = "ASCII 中文 123 🌍 test"; auto result = convert_string("UTF-8", "UTF-8", mixed); EXPECT_EQ(result, mixed); -} \ No newline at end of file +} diff --git a/atom/extra/inicpp/event_listener.hpp b/atom/extra/inicpp/event_listener.hpp index 1bd6f1fc..a11e9420 100644 --- a/atom/extra/inicpp/event_listener.hpp +++ b/atom/extra/inicpp/event_listener.hpp @@ -260,4 +260,4 @@ class EventManager { #endif // INICPP_CONFIG_EVENT_LISTENERS -#endif // ATOM_EXTRA_INICPP_EVENT_LISTENER_HPP \ No newline at end of file +#endif // ATOM_EXTRA_INICPP_EVENT_LISTENER_HPP diff --git a/atom/extra/inicpp/field.hpp b/atom/extra/inicpp/field.hpp index 9ed54235..a6d8ea1f 100644 --- a/atom/extra/inicpp/field.hpp +++ b/atom/extra/inicpp/field.hpp @@ -168,7 +168,7 @@ class IniField { class IniFieldPool { private: static boost::object_pool pool_; - + public: /** * @brief Allocate a new IniField from the pool. @@ -177,7 +177,7 @@ class IniFieldPool { static IniField* allocate() { return pool_.construct(); } - + /** * @brief Allocate a new IniField from the pool with an initial value. * @param value The initial value. @@ -188,7 +188,7 @@ class IniFieldPool { static IniField* allocate(StringType value) { return pool_.construct(value); } - + /** * @brief Free an IniField back to the pool. * @param field The field to free. diff --git a/atom/extra/inicpp/format_converter.hpp b/atom/extra/inicpp/format_converter.hpp index c9320f35..7fe3a717 100644 --- a/atom/extra/inicpp/format_converter.hpp +++ b/atom/extra/inicpp/format_converter.hpp @@ -341,4 +341,4 @@ inline IniFile FormatConverter::importFrom(const std::string& content, #endif // INICPP_CONFIG_FORMAT_CONVERSION -#endif // ATOM_EXTRA_INICPP_FORMAT_CONVERTER_HPP \ No newline at end of file +#endif // ATOM_EXTRA_INICPP_FORMAT_CONVERTER_HPP diff --git a/atom/extra/inicpp/inicpp.hpp b/atom/extra/inicpp/inicpp.hpp index a1f35966..d95cd49b 100644 --- a/atom/extra/inicpp/inicpp.hpp +++ b/atom/extra/inicpp/inicpp.hpp @@ -22,14 +22,14 @@ /** * @namespace inicpp * @brief 提供高性能、类型安全的INI配置文件解析功能 - * + * * 该库具有以下特点: * 1. 类型安全 - 通过模板获取强类型字段值 * 2. 线程安全 - 使用共享锁实现并发读写 * 3. 高性能 - 支持并行处理、内存池和Boost容器 * 4. 可扩展 - 支持自定义分隔符、转义字符和注释前缀 * 5. 丰富功能 - 支持嵌套段落、事件监听、路径查询、格式转换等 - * + * * 可通过宏控制功能开关: * - INICPP_CONFIG_USE_BOOST: 是否使用Boost库 * - INICPP_CONFIG_USE_BOOST_CONTAINERS: 是否使用Boost容器 diff --git a/atom/extra/inicpp/path_query.hpp b/atom/extra/inicpp/path_query.hpp index 162babfd..697c04df 100644 --- a/atom/extra/inicpp/path_query.hpp +++ b/atom/extra/inicpp/path_query.hpp @@ -162,4 +162,4 @@ class PathQuery { } // namespace inicpp -#endif // ATOM_EXTRA_INICPP_PATH_QUERY_HPP \ No newline at end of file +#endif // ATOM_EXTRA_INICPP_PATH_QUERY_HPP diff --git a/atom/extra/inicpp/section.hpp b/atom/extra/inicpp/section.hpp index d9f56566..65c12f2a 100644 --- a/atom/extra/inicpp/section.hpp +++ b/atom/extra/inicpp/section.hpp @@ -282,7 +282,7 @@ class IniSectionBase : public map_type { // 检查字段是否已存在 auto it = this->find(key); bool fieldExists = (it != this->end()); - + // 如果启用了事件监听,准备事件数据 #if INICPP_CONFIG_EVENT_LISTENERS std::string oldValue; @@ -293,7 +293,7 @@ class IniSectionBase : public map_type { // 设置或更新字段值 (*this)[key] = value; - + // 如果启用了事件监听,触发事件 #if INICPP_CONFIG_EVENT_LISTENERS // 准备事件数据 @@ -301,18 +301,18 @@ class IniSectionBase : public map_type { eventData.sectionName = sectionName_; eventData.fieldName = key; eventData.newValue = (*this)[key].template as(); - + if (fieldExists) { eventData.oldValue = oldValue; eventData.eventType = SectionEventType::FIELD_MODIFIED; } else { eventData.eventType = SectionEventType::FIELD_ADDED; } - + // 通知监听器 notifyListeners(eventData); #endif - + } catch (const std::exception& ex) { throw std::invalid_argument("Failed to set field '" + key + "': " + ex.what()); @@ -329,7 +329,7 @@ class IniSectionBase : public map_type { if (it == this->end()) { return false; } - + #if INICPP_CONFIG_EVENT_LISTENERS // 准备事件数据 SectionEventData eventData; @@ -338,15 +338,15 @@ class IniSectionBase : public map_type { eventData.oldValue = it->second.template as(); eventData.eventType = SectionEventType::FIELD_REMOVED; #endif - + // 删除字段 this->erase(it); - + #if INICPP_CONFIG_EVENT_LISTENERS // 通知监听器 notifyListeners(eventData); #endif - + return true; } @@ -369,10 +369,10 @@ class IniSectionBase : public map_type { eventData.sectionName = sectionName_; eventData.eventType = SectionEventType::SECTION_CLEARED; #endif - + // 清空所有字段 this->clear(); - + #if INICPP_CONFIG_EVENT_LISTENERS // 通知监听器 notifyListeners(eventData); diff --git a/atom/extra/pugixml/xml_builder.hpp b/atom/extra/pugixml/xml_builder.hpp index 16b78e3a..b3053d35 100644 --- a/atom/extra/pugixml/xml_builder.hpp +++ b/atom/extra/pugixml/xml_builder.hpp @@ -177,4 +177,4 @@ namespace literals { } // namespace literals -} // namespace atom::extra::pugixml \ No newline at end of file +} // namespace atom::extra::pugixml diff --git a/atom/extra/pugixml/xml_document.hpp b/atom/extra/pugixml/xml_document.hpp index 6f0da212..5f01bda3 100644 --- a/atom/extra/pugixml/xml_document.hpp +++ b/atom/extra/pugixml/xml_document.hpp @@ -232,4 +232,4 @@ class Document { } }; -} // namespace atom::extra::pugixml \ No newline at end of file +} // namespace atom::extra::pugixml diff --git a/atom/extra/pugixml/xml_node_wrapper.hpp b/atom/extra/pugixml/xml_node_wrapper.hpp index 7f2717c9..8bc36321 100644 --- a/atom/extra/pugixml/xml_node_wrapper.hpp +++ b/atom/extra/pugixml/xml_node_wrapper.hpp @@ -471,4 +471,4 @@ struct std::hash { size_t operator()(const atom::extra::pugixml::Node& node) const noexcept { return node.hash(); } -}; \ No newline at end of file +}; diff --git a/atom/extra/pugixml/xml_query.hpp b/atom/extra/pugixml/xml_query.hpp index 93016525..9b28c53d 100644 --- a/atom/extra/pugixml/xml_query.hpp +++ b/atom/extra/pugixml/xml_query.hpp @@ -219,4 +219,4 @@ void sort_children(Node& node, Compare&& comp) { } // namespace transform -} // namespace atom::extra::pugixml \ No newline at end of file +} // namespace atom::extra::pugixml diff --git a/atom/extra/spdlog/CMakeLists.txt b/atom/extra/spdlog/CMakeLists.txt index 9272bdfa..0a90245d 100644 --- a/atom/extra/spdlog/CMakeLists.txt +++ b/atom/extra/spdlog/CMakeLists.txt @@ -71,4 +71,4 @@ install(EXPORT modern_log_targets FILE modern_log_targets.cmake NAMESPACE modern_log:: DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/modern_log -) \ No newline at end of file +) diff --git a/atom/extra/spdlog/core/concepts.h b/atom/extra/spdlog/core/concepts.h index fb69ea58..69baa2be 100644 --- a/atom/extra/spdlog/core/concepts.h +++ b/atom/extra/spdlog/core/concepts.h @@ -75,4 +75,4 @@ template concept Range = std::ranges::range && Formattable>; -} // namespace modern_log \ No newline at end of file +} // namespace modern_log diff --git a/atom/extra/spdlog/core/context.cpp b/atom/extra/spdlog/core/context.cpp index e89f8701..8cf445d0 100644 --- a/atom/extra/spdlog/core/context.cpp +++ b/atom/extra/spdlog/core/context.cpp @@ -78,4 +78,4 @@ bool LogContext::empty() const { request_id_.empty() && custom_fields_.empty(); } -} // namespace modern_log \ No newline at end of file +} // namespace modern_log diff --git a/atom/extra/spdlog/core/context.h b/atom/extra/spdlog/core/context.h index 97c89e96..ecb6800e 100644 --- a/atom/extra/spdlog/core/context.h +++ b/atom/extra/spdlog/core/context.h @@ -151,4 +151,4 @@ class LogContext { bool empty() const; }; -} // namespace modern_log \ No newline at end of file +} // namespace modern_log diff --git a/atom/extra/spdlog/core/error.h b/atom/extra/spdlog/core/error.h index e55bca31..0d0e7927 100644 --- a/atom/extra/spdlog/core/error.h +++ b/atom/extra/spdlog/core/error.h @@ -127,4 +127,4 @@ using Result = std::expected; namespace std { template <> struct is_error_code_enum : true_type {}; -} // namespace std \ No newline at end of file +} // namespace std diff --git a/atom/extra/spdlog/core/test_context.h b/atom/extra/spdlog/core/test_context.h index 44dcf1bd..6768bc17 100644 --- a/atom/extra/spdlog/core/test_context.h +++ b/atom/extra/spdlog/core/test_context.h @@ -125,4 +125,4 @@ TEST(LogContextTest, EmptyReturnsTrueOnlyIfAllFieldsAreEmpty) { EXPECT_FALSE(ctx.empty()); ctx.clear(); EXPECT_TRUE(ctx.empty()); -} \ No newline at end of file +} diff --git a/atom/extra/spdlog/core/test_error.h b/atom/extra/spdlog/core/test_error.h index d00c02ee..bfaf3fd2 100644 --- a/atom/extra/spdlog/core/test_error.h +++ b/atom/extra/spdlog/core/test_error.h @@ -70,4 +70,4 @@ TEST(LogErrorTest, ErrorCodeEnumTrait) { // This test ensures LogError is recognized as an error_code_enum bool is_enum = std::is_error_code_enum::value; EXPECT_TRUE(is_enum); -} \ No newline at end of file +} diff --git a/atom/extra/spdlog/core/test_types.h b/atom/extra/spdlog/core/test_types.h index 58651e70..a814c6f7 100644 --- a/atom/extra/spdlog/core/test_types.h +++ b/atom/extra/spdlog/core/test_types.h @@ -139,4 +139,4 @@ TEST(LogConfigTest, AsyncConfig) { EXPECT_TRUE(config.async); EXPECT_EQ(config.async_queue_size, 4096u); EXPECT_EQ(config.async_thread_count, 4u); -} \ No newline at end of file +} diff --git a/atom/extra/spdlog/core/types.h b/atom/extra/spdlog/core/types.h index a130ad15..af1fac1f 100644 --- a/atom/extra/spdlog/core/types.h +++ b/atom/extra/spdlog/core/types.h @@ -147,4 +147,4 @@ struct LogStats { } }; -} // namespace modern_log \ No newline at end of file +} // namespace modern_log diff --git a/atom/extra/spdlog/events/event_system.cpp b/atom/extra/spdlog/events/event_system.cpp index 55f20eaf..08a74a5b 100644 --- a/atom/extra/spdlog/events/event_system.cpp +++ b/atom/extra/spdlog/events/event_system.cpp @@ -59,4 +59,4 @@ void LogEventSystem::clear_all_subscriptions() { callbacks_.clear(); } -} // namespace modern_log \ No newline at end of file +} // namespace modern_log diff --git a/atom/extra/spdlog/events/event_system.h b/atom/extra/spdlog/events/event_system.h index 8f7a59d2..1d5dff94 100644 --- a/atom/extra/spdlog/events/event_system.h +++ b/atom/extra/spdlog/events/event_system.h @@ -98,4 +98,4 @@ class LogEventSystem { void clear_all_subscriptions(); }; -} // namespace modern_log \ No newline at end of file +} // namespace modern_log diff --git a/atom/extra/spdlog/events/test_event_system.cpp b/atom/extra/spdlog/events/test_event_system.cpp index 9fd23173..4cdd4e51 100644 --- a/atom/extra/spdlog/events/test_event_system.cpp +++ b/atom/extra/spdlog/events/test_event_system.cpp @@ -122,4 +122,4 @@ TEST(LogEventSystemTest, SubscribeDifferentEventsAreIndependent) { sys.emit(LogEvent::logger_destroyed); EXPECT_EQ(called1, 1); EXPECT_EQ(called2, 1); -} \ No newline at end of file +} diff --git a/atom/extra/spdlog/filters/builtin_filters.cpp b/atom/extra/spdlog/filters/builtin_filters.cpp index e58d0994..92a5a24c 100644 --- a/atom/extra/spdlog/filters/builtin_filters.cpp +++ b/atom/extra/spdlog/filters/builtin_filters.cpp @@ -113,4 +113,4 @@ LogFilter::FilterFunc BuiltinFilters::duplicate_filter( }; } -} // namespace modern_log \ No newline at end of file +} // namespace modern_log diff --git a/atom/extra/spdlog/filters/builtin_filters.h b/atom/extra/spdlog/filters/builtin_filters.h index 8d749633..1f349ea4 100644 --- a/atom/extra/spdlog/filters/builtin_filters.h +++ b/atom/extra/spdlog/filters/builtin_filters.h @@ -106,4 +106,4 @@ class BuiltinFilters { std::chrono::seconds window = std::chrono::seconds(60)); }; -} // namespace modern_log \ No newline at end of file +} // namespace modern_log diff --git a/atom/extra/spdlog/filters/filter.cpp b/atom/extra/spdlog/filters/filter.cpp index aceafe90..c32f8095 100644 --- a/atom/extra/spdlog/filters/filter.cpp +++ b/atom/extra/spdlog/filters/filter.cpp @@ -28,4 +28,4 @@ size_t LogFilter::filter_count() const { return filters_.size(); } -} // namespace modern_log \ No newline at end of file +} // namespace modern_log diff --git a/atom/extra/spdlog/filters/filter.h b/atom/extra/spdlog/filters/filter.h index 769e0599..8f10ce4b 100644 --- a/atom/extra/spdlog/filters/filter.h +++ b/atom/extra/spdlog/filters/filter.h @@ -68,4 +68,4 @@ class LogFilter { size_t filter_count() const; }; -} // namespace modern_log \ No newline at end of file +} // namespace modern_log diff --git a/atom/extra/spdlog/filters/test_builtin_filters.cpp b/atom/extra/spdlog/filters/test_builtin_filters.cpp index d0264253..34612542 100644 --- a/atom/extra/spdlog/filters/test_builtin_filters.cpp +++ b/atom/extra/spdlog/filters/test_builtin_filters.cpp @@ -419,4 +419,4 @@ TEST(BuiltinFiltersTest, DuplicateFilterSuppressesDuplicatesWithinWindow) { std::this_thread::sleep_for(std::chrono::seconds(2)); EXPECT_TRUE(filter("msg1", Level::info, LogContext{})); EXPECT_TRUE(filter("msg2", Level::info, LogContext{})); -} \ No newline at end of file +} diff --git a/atom/extra/spdlog/logger/logger.cpp b/atom/extra/spdlog/logger/logger.cpp index 29158865..9faf7c42 100644 --- a/atom/extra/spdlog/logger/logger.cpp +++ b/atom/extra/spdlog/logger/logger.cpp @@ -116,4 +116,4 @@ void Logger::emit_event(LogEvent event, const std::any& data) { } } -} // namespace modern_log \ No newline at end of file +} // namespace modern_log diff --git a/atom/extra/spdlog/logger/logger.h b/atom/extra/spdlog/logger/logger.h index 7fc82be4..005ad600 100644 --- a/atom/extra/spdlog/logger/logger.h +++ b/atom/extra/spdlog/logger/logger.h @@ -346,4 +346,4 @@ class Logger { void emit_event(LogEvent event, const std::any& data = {}); }; -} // namespace modern_log \ No newline at end of file +} // namespace modern_log diff --git a/atom/extra/spdlog/logger/manager.cpp b/atom/extra/spdlog/logger/manager.cpp index 6996be04..e24cdf17 100644 --- a/atom/extra/spdlog/logger/manager.cpp +++ b/atom/extra/spdlog/logger/manager.cpp @@ -244,4 +244,4 @@ void LogManager::setup_async_logging(const LogConfig& config) { } } -} // namespace modern_log \ No newline at end of file +} // namespace modern_log diff --git a/atom/extra/spdlog/logger/manager.h b/atom/extra/spdlog/logger/manager.h index a48b936c..67e2b2ce 100644 --- a/atom/extra/spdlog/logger/manager.h +++ b/atom/extra/spdlog/logger/manager.h @@ -216,4 +216,4 @@ class LogManager { void setup_async_logging(const LogConfig& config); }; -} // namespace modern_log \ No newline at end of file +} // namespace modern_log diff --git a/atom/extra/spdlog/logger/test_logger.cpp b/atom/extra/spdlog/logger/test_logger.cpp index 626c0d16..f15202b1 100644 --- a/atom/extra/spdlog/logger/test_logger.cpp +++ b/atom/extra/spdlog/logger/test_logger.cpp @@ -51,7 +51,7 @@ class LoggerTest : public ::testing::Test { auto sink = std::make_shared(*log_stream); spdlog_logger = std::make_shared("test_logger", sink); spdlog_logger->set_level(spdlog::level::trace); - + mock_event_system = std::make_unique>(); event_system_ptr = mock_event_system.get(); } @@ -73,9 +73,9 @@ class LoggerTest : public ::testing::Test { TEST_F(LoggerTest, ConstructorInitializesComponents) { EXPECT_CALL(*mock_event_system, emit(LogEvent::logger_created, _)); - + Logger logger(spdlog_logger, event_system_ptr); - + EXPECT_EQ(logger.get_spdlog_logger(), spdlog_logger); EXPECT_EQ(logger.get_log_type(), LogType::general); EXPECT_TRUE(logger.get_context().empty()); @@ -83,14 +83,14 @@ TEST_F(LoggerTest, ConstructorInitializesComponents) { TEST_F(LoggerTest, BasicLoggingAtAllLevels) { Logger logger(spdlog_logger); - + logger.trace("trace message"); logger.debug("debug message"); logger.info("info message"); logger.warn("warn message"); logger.error("error message"); logger.critical("critical message"); - + std::string output = getLogOutput(); EXPECT_NE(output.find("trace message"), std::string::npos); EXPECT_NE(output.find("debug message"), std::string::npos); @@ -154,14 +154,14 @@ TEST_F(LoggerTest, ContextClearing) { TEST_F(LoggerTest, StructuredLogging) { Logger logger(spdlog_logger); - + StructuredData data; data.add("key1", "value1"); data.add("key2", 42); data.add("key3", true); - + logger.log_structured(Level::info, data); - + std::string output = getLogOutput(); EXPECT_NE(output.find("STRUCTURED:"), std::string::npos); EXPECT_NE(output.find("key1"), std::string::npos); @@ -172,10 +172,10 @@ TEST_F(LoggerTest, StructuredLogging) { TEST_F(LoggerTest, ExceptionLogging) { Logger logger(spdlog_logger); - + std::runtime_error ex("test exception"); logger.log_exception(Level::error, ex, "test context"); - + std::string output = getLogOutput(); EXPECT_NE(output.find("Exception: test exception"), std::string::npos); EXPECT_NE(output.find("Context: test context"), std::string::npos); @@ -184,10 +184,10 @@ TEST_F(LoggerTest, ExceptionLogging) { TEST_F(LoggerTest, ConditionalLogging) { Logger logger(spdlog_logger); - + logger.log_if(true, Level::info, "should log"); logger.log_if(false, Level::info, "should not log"); - + std::string output = getLogOutput(); EXPECT_NE(output.find("should log"), std::string::npos); EXPECT_EQ(output.find("should not log"), std::string::npos); @@ -195,12 +195,12 @@ TEST_F(LoggerTest, ConditionalLogging) { TEST_F(LoggerTest, ScopedTiming) { Logger logger(spdlog_logger); - + { auto timer = logger.time_scope("test_operation"); std::this_thread::sleep_for(std::chrono::milliseconds(1)); } - + std::string output = getLogOutput(); EXPECT_NE(output.find("test_operation took"), std::string::npos); EXPECT_NE(output.find("μs"), std::string::npos); @@ -208,9 +208,9 @@ TEST_F(LoggerTest, ScopedTiming) { TEST_F(LoggerTest, BatchLogging) { Logger logger(spdlog_logger); - + logger.log_batch(Level::info, "message1", "message2", "message3"); - + std::string output = getLogOutput(); EXPECT_NE(output.find("message1"), std::string::npos); EXPECT_NE(output.find("message2"), std::string::npos); @@ -219,10 +219,10 @@ TEST_F(LoggerTest, BatchLogging) { TEST_F(LoggerTest, RangeLogging) { Logger logger(spdlog_logger); - + std::vector numbers = {1, 2, 3, 4, 5}; logger.log_range(Level::info, "numbers", numbers); - + std::string output = getLogOutput(); EXPECT_NE(output.find("numbers"), std::string::npos); EXPECT_NE(output.find("1"), std::string::npos); @@ -232,12 +232,12 @@ TEST_F(LoggerTest, RangeLogging) { TEST_F(LoggerTest, LogLevelFiltering) { Logger logger(spdlog_logger); logger.set_level(Level::warn); - + logger.debug("debug message"); logger.info("info message"); logger.warn("warn message"); logger.error("error message"); - + std::string output = getLogOutput(); EXPECT_EQ(output.find("debug message"), std::string::npos); EXPECT_EQ(output.find("info message"), std::string::npos); @@ -247,9 +247,9 @@ TEST_F(LoggerTest, LogLevelFiltering) { TEST_F(LoggerTest, ShouldLogChecking) { Logger logger(spdlog_logger); - + logger.set_level(Level::warn); - + EXPECT_FALSE(logger.should_log(Level::trace)); EXPECT_FALSE(logger.should_log(Level::debug)); EXPECT_FALSE(logger.should_log(Level::info)); @@ -260,11 +260,11 @@ TEST_F(LoggerTest, ShouldLogChecking) { TEST_F(LoggerTest, StatisticsTracking) { Logger logger(spdlog_logger); - + logger.info("message1"); logger.warn("message2"); logger.error("message3"); - + const auto& stats = logger.get_stats(); EXPECT_EQ(stats.total_logs.load(), 3u); EXPECT_EQ(stats.failed_logs.load(), 0u); @@ -272,20 +272,20 @@ TEST_F(LoggerTest, StatisticsTracking) { TEST_F(LoggerTest, StatisticsReset) { Logger logger(spdlog_logger); - + logger.info("message"); EXPECT_GT(logger.get_stats().total_logs.load(), 0u); - + logger.reset_stats(); EXPECT_EQ(logger.get_stats().total_logs.load(), 0u); } TEST_F(LoggerTest, FlushOperation) { Logger logger(spdlog_logger); - + logger.info("test message"); logger.flush(); - + // Verify message is in output after flush std::string output = getLogOutput(); EXPECT_NE(output.find("test message"), std::string::npos); @@ -293,21 +293,21 @@ TEST_F(LoggerTest, FlushOperation) { TEST_F(LoggerTest, LogTypeManagement) { Logger logger(spdlog_logger); - + EXPECT_EQ(logger.get_log_type(), LogType::general); - + logger.set_log_type(LogType::security); EXPECT_EQ(logger.get_log_type(), LogType::security); - + logger.set_log_type(LogType::performance); EXPECT_EQ(logger.get_log_type(), LogType::performance); } TEST_F(LoggerTest, EventSystemIntegration) { EXPECT_CALL(*mock_event_system, emit(LogEvent::logger_created, _)); - + Logger logger(spdlog_logger, event_system_ptr); - + // Verify constructor emitted logger_created event ::testing::Mock::VerifyAndClearExpectations(mock_event_system.get()); } @@ -374,13 +374,13 @@ TEST_F(LoggerTest, ContextualLogging) { TEST_F(LoggerTest, SetFlushLevel) { Logger logger(spdlog_logger); - + logger.set_flush_level(Level::warn); - + // This test mainly verifies the function doesn't crash logger.info("info message"); logger.warn("warn message"); - + std::string output = getLogOutput(); EXPECT_NE(output.find("info message"), std::string::npos); EXPECT_NE(output.find("warn message"), std::string::npos); @@ -388,19 +388,19 @@ TEST_F(LoggerTest, SetFlushLevel) { TEST_F(LoggerTest, FilteringIntegration) { Logger logger(spdlog_logger); - + // Add a filter that blocks messages containing "secret" logger.add_filter([](const std::string& msg, Level, const LogContext&) { return msg.find("secret") == std::string::npos; }); - + logger.info("normal message"); logger.info("secret message"); - + std::string output = getLogOutput(); EXPECT_NE(output.find("normal message"), std::string::npos); EXPECT_EQ(output.find("secret message"), std::string::npos); - + // Verify filtered message is counted in stats const auto& stats = logger.get_stats(); EXPECT_EQ(stats.filtered_logs.load(), 1u); @@ -408,16 +408,16 @@ TEST_F(LoggerTest, FilteringIntegration) { TEST_F(LoggerTest, SamplingIntegration) { Logger logger(spdlog_logger); - + // Set sampling to 0% (drop everything) logger.set_sampling(SamplingStrategy::uniform, 0.0); - + logger.info("sampled message 1"); logger.info("sampled message 2"); - + std::string output = getLogOutput(); EXPECT_EQ(output.find("sampled message"), std::string::npos); - + // Verify sampled messages are counted in stats const auto& stats = logger.get_stats(); EXPECT_EQ(stats.sampled_logs.load(), 2u); @@ -427,11 +427,11 @@ TEST_F(LoggerTest, ErrorHandlingInLogInternal) { // Create a logger with a bad sink to simulate errors auto bad_sink = std::make_shared(std::cout); auto bad_logger = std::make_shared("bad_logger", bad_sink); - + Logger logger(bad_logger); - + // This should not crash even if the underlying logger fails logger.info("test message"); - + // The test mainly verifies no exceptions are thrown -} \ No newline at end of file +} diff --git a/atom/extra/spdlog/logger/test_manager.cpp b/atom/extra/spdlog/logger/test_manager.cpp index 37002285..78d7ea34 100644 --- a/atom/extra/spdlog/logger/test_manager.cpp +++ b/atom/extra/spdlog/logger/test_manager.cpp @@ -466,4 +466,4 @@ TEST_F(LogManagerTest, LoggerCreationPerformance) { // Should create 100 loggers reasonably quickly (adjust threshold as needed) EXPECT_LT(duration.count(), 1000); // Less than 1 second -} \ No newline at end of file +} diff --git a/atom/extra/spdlog/modern_log.h b/atom/extra/spdlog/modern_log.h index e6b0bfca..476a9137 100644 --- a/atom/extra/spdlog/modern_log.h +++ b/atom/extra/spdlog/modern_log.h @@ -25,4 +25,4 @@ #define LOG_TIME_SCOPE(name) auto _timer = modern_log::LogManager::default_logger().time_scope(name) -#define LOG_WITH_CONTEXT(ctx) modern_log::LogManager::default_logger().with_context(ctx) \ No newline at end of file +#define LOG_WITH_CONTEXT(ctx) modern_log::LogManager::default_logger().with_context(ctx) diff --git a/atom/extra/spdlog/sampling/sampler.cpp b/atom/extra/spdlog/sampling/sampler.cpp index 09309531..149ed2aa 100644 --- a/atom/extra/spdlog/sampling/sampler.cpp +++ b/atom/extra/spdlog/sampling/sampler.cpp @@ -116,4 +116,4 @@ double LogSampler::get_system_load() const { return dis(gen) * 0.5; } -} // namespace modern_log \ No newline at end of file +} // namespace modern_log diff --git a/atom/extra/spdlog/sampling/sampler.h b/atom/extra/spdlog/sampling/sampler.h index c6ad05c1..caa70328 100644 --- a/atom/extra/spdlog/sampling/sampler.h +++ b/atom/extra/spdlog/sampling/sampler.h @@ -97,4 +97,4 @@ class LogSampler { double get_system_load() const; }; -} // namespace modern_log \ No newline at end of file +} // namespace modern_log diff --git a/atom/extra/spdlog/sampling/test_sampler.cpp b/atom/extra/spdlog/sampling/test_sampler.cpp index 88dd7f92..754f4c4b 100644 --- a/atom/extra/spdlog/sampling/test_sampler.cpp +++ b/atom/extra/spdlog/sampling/test_sampler.cpp @@ -146,4 +146,4 @@ TEST(LogSamplerTest, ThreadSafety) { EXPECT_NEAR(kept, 200, 20); EXPECT_NEAR(dropped, 200, 20); EXPECT_EQ(sampler.get_dropped_count(), dropped); -} \ No newline at end of file +} diff --git a/atom/extra/spdlog/utils/archiver.cpp b/atom/extra/spdlog/utils/archiver.cpp index d5be26c9..5828b43f 100644 --- a/atom/extra/spdlog/utils/archiver.cpp +++ b/atom/extra/spdlog/utils/archiver.cpp @@ -173,4 +173,4 @@ std::string LogArchiver::generate_archive_name( return pattern; } -} // namespace modern_log \ No newline at end of file +} // namespace modern_log diff --git a/atom/extra/spdlog/utils/archiver.h b/atom/extra/spdlog/utils/archiver.h index 17bd1047..5084c2fc 100644 --- a/atom/extra/spdlog/utils/archiver.h +++ b/atom/extra/spdlog/utils/archiver.h @@ -162,4 +162,4 @@ class LogArchiver { const std::filesystem::path& original) const; }; -} // namespace modern_log \ No newline at end of file +} // namespace modern_log diff --git a/atom/extra/spdlog/utils/structured_data.cpp b/atom/extra/spdlog/utils/structured_data.cpp index 95c03b40..1216cb1d 100644 --- a/atom/extra/spdlog/utils/structured_data.cpp +++ b/atom/extra/spdlog/utils/structured_data.cpp @@ -102,4 +102,4 @@ std::string StructuredData::any_to_string(const std::any& value) const { return "null"; } -} // namespace modern_log \ No newline at end of file +} // namespace modern_log diff --git a/atom/extra/spdlog/utils/structured_data.h b/atom/extra/spdlog/utils/structured_data.h index 763a9412..ca515dc3 100644 --- a/atom/extra/spdlog/utils/structured_data.h +++ b/atom/extra/spdlog/utils/structured_data.h @@ -145,4 +145,4 @@ class StructuredData { std::string any_to_string(const std::any& value) const; }; -} // namespace modern_log \ No newline at end of file +} // namespace modern_log diff --git a/atom/extra/spdlog/utils/test_archiver.cpp b/atom/extra/spdlog/utils/test_archiver.cpp index 14a3cffa..0f2688ed 100644 --- a/atom/extra/spdlog/utils/test_archiver.cpp +++ b/atom/extra/spdlog/utils/test_archiver.cpp @@ -146,4 +146,4 @@ TEST_F(LogArchiverTest, CompressFileHandlesNonexistentFileGracefully) { TEST_F(LogArchiverTest, DecompressFileHandlesNonexistentFileGracefully) { LogArchiver archiver(temp_dir); EXPECT_FALSE(archiver.decompress_file(temp_dir / "no_such_file.gz")); -} \ No newline at end of file +} diff --git a/atom/extra/spdlog/utils/test_timer.cpp b/atom/extra/spdlog/utils/test_timer.cpp index bd1001b1..57352b5a 100644 --- a/atom/extra/spdlog/utils/test_timer.cpp +++ b/atom/extra/spdlog/utils/test_timer.cpp @@ -121,4 +121,4 @@ TEST(BenchmarkTest, ReportDoesNothingIfLoggerNullOrEmpty) { auto logger = std::make_shared(); bench.report(logger.get()); EXPECT_TRUE(logger->entries.empty()); -} \ No newline at end of file +} diff --git a/atom/extra/spdlog/utils/timer.cpp b/atom/extra/spdlog/utils/timer.cpp index 80a0c493..14bd080d 100644 --- a/atom/extra/spdlog/utils/timer.cpp +++ b/atom/extra/spdlog/utils/timer.cpp @@ -99,4 +99,4 @@ void Benchmark::report(Logger* logger) const { std::format(" Std Dev: {:.2f}μs", stats.std_dev)); } -} // namespace modern_log \ No newline at end of file +} // namespace modern_log diff --git a/atom/extra/spdlog/utils/timer.h b/atom/extra/spdlog/utils/timer.h index f2d1b1ab..86336b0c 100644 --- a/atom/extra/spdlog/utils/timer.h +++ b/atom/extra/spdlog/utils/timer.h @@ -143,4 +143,4 @@ class Benchmark { void report(Logger* logger) const; }; -} // namespace modern_log \ No newline at end of file +} // namespace modern_log diff --git a/atom/extra/uv/coro.hpp b/atom/extra/uv/coro.hpp index 5a6b40ad..9607f061 100644 --- a/atom/extra/uv/coro.hpp +++ b/atom/extra/uv/coro.hpp @@ -1040,4 +1040,4 @@ inline FileSystem make_file_system() { } } // namespace uv_coro -#endif // ATOM_EXTRA_UV_CORO_HPP \ No newline at end of file +#endif // ATOM_EXTRA_UV_CORO_HPP diff --git a/atom/extra/uv/message_bus.cpp b/atom/extra/uv/message_bus.cpp index c2674a67..737174b4 100644 --- a/atom/extra/uv/message_bus.cpp +++ b/atom/extra/uv/message_bus.cpp @@ -373,4 +373,4 @@ Result> MessageAwaiter::await_resume() { return future.get(); } -} // namespace msgbus \ No newline at end of file +} // namespace msgbus diff --git a/atom/extra/uv/message_bus.hpp b/atom/extra/uv/message_bus.hpp index 4b8f0520..5c4c5e47 100644 --- a/atom/extra/uv/message_bus.hpp +++ b/atom/extra/uv/message_bus.hpp @@ -117,4 +117,4 @@ struct MessageAwaiter { std::shared_ptr>>> promise_; }; -} // namespace msgbus \ No newline at end of file +} // namespace msgbus diff --git a/atom/extra/uv/subprocess.cpp b/atom/extra/uv/subprocess.cpp index a3b39f37..50a3099a 100644 --- a/atom/extra/uv/subprocess.cpp +++ b/atom/extra/uv/subprocess.cpp @@ -701,4 +701,4 @@ void UvProcess::reset() { void UvProcess::setErrorCallback(ErrorCallback error_callback) { std::lock_guard lock(mutex_); error_callback_ = std::move(error_callback); -} \ No newline at end of file +} diff --git a/atom/extra/uv/subprocess.hpp b/atom/extra/uv/subprocess.hpp index 4cfc340f..8d18f096 100644 --- a/atom/extra/uv/subprocess.hpp +++ b/atom/extra/uv/subprocess.hpp @@ -256,4 +256,4 @@ class UvProcess { ErrorCallback error_callback_; }; -#endif // ATOM_EXTRA_UV_SUBPROCESS_HPP \ No newline at end of file +#endif // ATOM_EXTRA_UV_SUBPROCESS_HPP diff --git a/atom/image/fits_data.hpp b/atom/image/fits_data.hpp index a70c8630..e43ee00e 100644 --- a/atom/image/fits_data.hpp +++ b/atom/image/fits_data.hpp @@ -42,10 +42,10 @@ class FITSDataException : public std::system_error { public: explicit FITSDataException(FITSDataErrorCode code, const std::string& message = "") : std::system_error(make_error_code(code), message) {} - + explicit FITSDataException(const std::string& message) : std::system_error(make_error_code(FITSDataErrorCode::InternalError), message) {} - + [[nodiscard]] FITSDataErrorCode errorCode() const noexcept { return static_cast(code().value()); } @@ -96,7 +96,7 @@ class FITSData { * @param chunkSize The size of each chunk to read (default 1MB). * @throws FITSDataException If there is an error reading data */ - virtual void readDataChunked(std::ifstream& file, int64_t dataSize, + virtual void readDataChunked(std::ifstream& file, int64_t dataSize, size_t chunkSize = 1024 * 1024) = 0; /** @@ -178,7 +178,7 @@ class FITSData { protected: DataProgressCallback progressCallback; ///< Callback for progress reporting - + /** * @brief Reports progress to the registered callback, if any. * @param progress Progress value (0.0 to 1.0). @@ -240,7 +240,7 @@ class TypedFITSData : public FITSData { * @param chunkSize The size of each chunk to read (default 1MB). * @throws FITSDataException If there is an error reading data */ - void readDataChunked(std::ifstream& file, int64_t dataSize, + void readDataChunked(std::ifstream& file, int64_t dataSize, size_t chunkSize = 1024 * 1024) override; /** diff --git a/atom/image/fits_file.hpp b/atom/image/fits_file.hpp index 8673db89..79217988 100644 --- a/atom/image/fits_file.hpp +++ b/atom/image/fits_file.hpp @@ -52,10 +52,10 @@ class FITSFileException : public std::system_error { public: explicit FITSFileException(FITSErrorCode code, const std::string& message = "") : std::system_error(make_error_code(code), message) {} - + explicit FITSFileException(const std::string& message) : std::system_error(make_error_code(FITSErrorCode::InternalError), message) {} - + [[nodiscard]] FITSErrorCode errorCode() const noexcept { return static_cast(code().value()); } @@ -214,7 +214,7 @@ class FITSFile { * @param callback The callback function to set. */ void setProgressCallback(ProgressCallback callback) noexcept; - + /** * @brief Reads a FITS file from the specified filename with options. * @param filename The name of the file to read. @@ -222,7 +222,7 @@ class FITSFile { * @param validateData Whether to validate data after reading. * @throws FITSFileException if file cannot be opened or read */ - void readFITS(const std::string& filename, bool useMmap = false, + void readFITS(const std::string& filename, bool useMmap = false, bool validateData = true); /** @@ -232,7 +232,7 @@ class FITSFile { * @param validateData Whether to validate data after reading. * @return A future that can be waited on for completion. */ - [[nodiscard]] std::future readFITSAsync(const std::string& filename, + [[nodiscard]] std::future readFITSAsync(const std::string& filename, bool useMmap = false, bool validateData = true); @@ -240,14 +240,14 @@ class FITSFile { std::vector> hdus; ///< Vector of unique pointers to HDUs. ProgressCallback progressCallback; ///< Callback for progress reporting. - + /** * @brief Reports progress to the registered callback, if any. * @param progress Progress value (0.0 to 1.0). * @param status Status message. */ void reportProgress(float progress, const std::string& status) const; - + /** * @brief Reads a FITS file using memory-mapped I/O. * @param filename The name of the file to read. diff --git a/atom/image/fits_header.cpp b/atom/image/fits_header.cpp index 202b462b..fdc5e73b 100644 --- a/atom/image/fits_header.cpp +++ b/atom/image/fits_header.cpp @@ -339,4 +339,4 @@ std::vector FITSHeader::getAllKeywords() const { } return keywords; -} \ No newline at end of file +} diff --git a/atom/image/fits_header.hpp b/atom/image/fits_header.hpp index d5f6b996..fbbf21db 100644 --- a/atom/image/fits_header.hpp +++ b/atom/image/fits_header.hpp @@ -251,21 +251,21 @@ class FITSHeader { /** * @brief Removes all comments from the header - * + * * @return The number of comments removed */ size_t clearComments() noexcept; /** * @brief Get the number of records in the header - * + * * @return The number of keyword records */ [[nodiscard]] size_t size() const noexcept { return records.size(); } /** * @brief Check if the header is empty - * + * * @return true if there are no records, false otherwise */ [[nodiscard]] bool empty() const noexcept { return records.empty(); } @@ -286,11 +286,11 @@ class FITSHeader { /** * @brief Finds a keyword in the records - * + * * @param keyword The keyword to find * @return The index of the keyword record, or std::string::npos if not found */ [[nodiscard]] size_t findKeywordIndex(std::string_view keyword) const noexcept; }; -#endif // ATOM_IMAGE_FITS_HEADER_HPP \ No newline at end of file +#endif // ATOM_IMAGE_FITS_HEADER_HPP diff --git a/atom/image/fits_utils.cpp b/atom/image/fits_utils.cpp index 1a3e5e52..4a1084ce 100644 --- a/atom/image/fits_utils.cpp +++ b/atom/image/fits_utils.cpp @@ -1331,4 +1331,4 @@ int processFitsDirectory(const std::string& inputDir, #endif // ATOM_ENABLE_OPENCV } // namespace image -} // namespace atom \ No newline at end of file +} // namespace atom diff --git a/atom/image/fits_utils.hpp b/atom/image/fits_utils.hpp index 3826d20f..2a47e468 100644 --- a/atom/image/fits_utils.hpp +++ b/atom/image/fits_utils.hpp @@ -412,4 +412,4 @@ std::optional> getFitsImageInfo( } // namespace image } // namespace atom -#endif // ATOM_IMAGE_FITS_UTILS_HPP \ No newline at end of file +#endif // ATOM_IMAGE_FITS_UTILS_HPP diff --git a/atom/image/ocr/install_ocr_dependencies.sh b/atom/image/ocr/install_ocr_dependencies.sh index 9c9082fe..9ac5ec92 100644 --- a/atom/image/ocr/install_ocr_dependencies.sh +++ b/atom/image/ocr/install_ocr_dependencies.sh @@ -61,7 +61,7 @@ detect_os() { else OS="unknown" fi - + log "Detected operating system: $OS" } @@ -75,10 +75,10 @@ create_directories() { # Download models download_models() { log "Downloading OCR models and resources..." - + # Create models directory if it doesn't exist mkdir -p "$MODELS_DIR" - + # Download EAST text detection model log "Downloading EAST text detection model..." if command -v wget &> /dev/null; then @@ -100,7 +100,7 @@ download_models() { error "Download URL: https://github.com/oyyd/frozen_east_text_detection.pb/raw/master/frozen_east_text_detection.pb" error "Save to: $MODELS_DIR/east_text_detection.pb" fi - + # Download super resolution model log "Downloading ESPCN super resolution model..." if command -v wget &> /dev/null; then @@ -122,7 +122,7 @@ download_models() { error "Download URL: https://github.com/fannymonori/TF-ESPCN/raw/master/export/ESPCN_x4.pb" error "Save to: $MODELS_DIR/ESPCN_x4.pb" fi - + # Download English dictionary for spell checking log "Downloading English dictionary for spell checking..." if command -v wget &> /dev/null; then @@ -144,7 +144,7 @@ download_models() { error "Download URL: https://raw.githubusercontent.com/dwyl/english-words/master/words.txt" error "Save to: $DICT_DIR/english.txt" fi - + # Check if files were downloaded successfully if [ -f "$MODELS_DIR/east_text_detection.pb" ] && [ -f "$MODELS_DIR/ESPCN_x4.pb" ]; then success "Models downloaded successfully" @@ -156,13 +156,13 @@ download_models() { # Install dependencies on Debian/Ubuntu install_debian() { log "Installing dependencies on Debian/Ubuntu..." - + # Update package lists sudo apt-get update - + # Install build tools and basic dependencies sudo apt-get install -y build-essential cmake git pkg-config wget curl - + # Install OpenCV dependencies sudo apt-get install -y \ libopencv-dev \ @@ -180,7 +180,7 @@ install_debian() { gfortran \ openexr \ libatlas-base-dev - + # Install Tesseract OCR and language data sudo apt-get install -y \ tesseract-ocr \ @@ -188,26 +188,26 @@ install_debian() { libleptonica-dev \ tesseract-ocr-eng \ tesseract-ocr-osd - + # Optional: Install additional language packs sudo apt-get install -y \ tesseract-ocr-fra \ tesseract-ocr-deu \ tesseract-ocr-spa - + success "Dependencies installed successfully on Debian/Ubuntu" } # Install dependencies on Fedora install_fedora() { log "Installing dependencies on Fedora..." - + # Update package lists sudo dnf update -y - + # Install build tools and basic dependencies sudo dnf install -y gcc-c++ cmake git pkgconfig wget curl - + # Install OpenCV and its dependencies sudo dnf install -y \ opencv \ @@ -222,42 +222,42 @@ install_fedora() { lapack-devel \ atlas-devel \ openexr-devel - + # Install Tesseract OCR and language data sudo dnf install -y \ tesseract \ tesseract-devel \ tesseract-langpack-eng \ leptonica-devel - + # Optional: Install additional language packs sudo dnf install -y \ tesseract-langpack-fra \ tesseract-langpack-deu \ tesseract-langpack-spa - + success "Dependencies installed successfully on Fedora" } # Install dependencies on RHEL/CentOS install_rhel() { log "Installing dependencies on RHEL/CentOS..." - + # Enable EPEL repository sudo yum install -y epel-release - + # Update package lists sudo yum update -y - + # Install build tools and basic dependencies sudo yum groupinstall -y "Development Tools" sudo yum install -y cmake3 git pkgconfig wget curl - + # Create link for cmake if needed if ! command -v cmake &> /dev/null && command -v cmake3 &> /dev/null; then sudo ln -s /usr/bin/cmake3 /usr/bin/cmake fi - + # Install OpenCV dependencies sudo yum install -y \ opencv \ @@ -270,34 +270,34 @@ install_rhel() { libtiff-devel \ atlas-devel \ openexr-devel - + # Install Tesseract OCR and language data sudo yum install -y \ tesseract \ tesseract-devel \ leptonica-devel - + # Download and install English language data if [ ! -d "/usr/share/tesseract/tessdata" ]; then sudo mkdir -p /usr/share/tesseract/tessdata fi - + wget -O /tmp/eng.traineddata https://github.com/tesseract-ocr/tessdata/raw/4.0.0/eng.traineddata sudo mv /tmp/eng.traineddata /usr/share/tesseract/tessdata/ - + success "Dependencies installed successfully on RHEL/CentOS" } # Install dependencies on Arch Linux install_arch() { log "Installing dependencies on Arch Linux..." - + # Update package database sudo pacman -Syu --noconfirm - + # Install build tools and basic dependencies sudo pacman -S --noconfirm base-devel cmake git pkgconf wget curl - + # Install OpenCV and its dependencies sudo pacman -S --noconfirm \ opencv \ @@ -310,33 +310,33 @@ install_arch() { openblas \ lapack \ openexr - + # Install Tesseract OCR and language data sudo pacman -S --noconfirm \ tesseract \ tesseract-data-eng \ leptonica - + # Optional: Install additional language data sudo pacman -S --noconfirm \ tesseract-data-fra \ tesseract-data-deu \ tesseract-data-spa - + success "Dependencies installed successfully on Arch Linux" } # Install dependencies on openSUSE install_suse() { log "Installing dependencies on openSUSE..." - + # Update package database sudo zypper refresh - + # Install build tools and basic dependencies sudo zypper install -y -t pattern devel_basis sudo zypper install -y cmake git pkgconfig wget curl - + # Install OpenCV and its dependencies sudo zypper install -y \ opencv \ @@ -350,27 +350,27 @@ install_suse() { blas-devel \ lapack-devel \ OpenEXR-devel - + # Install Tesseract OCR and language data sudo zypper install -y \ tesseract-ocr \ tesseract-ocr-devel \ tesseract-ocr-traineddata-english \ leptonica-devel - + # Optional: Install additional language data sudo zypper install -y \ tesseract-ocr-traineddata-french \ tesseract-ocr-traineddata-german \ tesseract-ocr-traineddata-spanish - + success "Dependencies installed successfully on openSUSE" } # Install dependencies on macOS using Homebrew install_macos() { log "Installing dependencies on macOS..." - + # Check if Homebrew is installed, install if not if ! command -v brew &> /dev/null; then log "Installing Homebrew..." @@ -379,26 +379,26 @@ install_macos() { log "Homebrew already installed, updating..." brew update fi - + # Install build tools and basic dependencies brew install cmake git wget curl - + # Install OpenCV and its dependencies brew install opencv - + # Install Tesseract OCR and language data brew install tesseract - + # Optional: Install additional language data brew install tesseract-lang - + success "Dependencies installed successfully on macOS" } # Install dependencies on Windows using Chocolatey and vcpkg create_windows_script() { log "Creating Windows installation script..." - + cat > Install-OCRDependencies.ps1 << 'EOF' # Enhanced OCR System - Windows Dependency Installer # Run this script with administrator privileges @@ -413,31 +413,31 @@ $VCPKG_DIR = "C:\vcpkg" # Create directories function Create-Directories { Write-Host "Creating necessary directories..." - + if (-not (Test-Path $MODELS_DIR)) { New-Item -ItemType Directory -Force -Path $MODELS_DIR | Out-Null } if (-not (Test-Path $CACHE_DIR)) { New-Item -ItemType Directory -Force -Path $CACHE_DIR | Out-Null } if (-not (Test-Path $LOG_DIR)) { New-Item -ItemType Directory -Force -Path $LOG_DIR | Out-Null } if (-not (Test-Path $DICT_DIR)) { New-Item -ItemType Directory -Force -Path $DICT_DIR | Out-Null } - + Write-Host "Directories created successfully" -ForegroundColor Green } # Download models function Download-Models { Write-Host "Downloading OCR models and resources..." - + # Download EAST text detection model Write-Host "Downloading EAST text detection model..." Invoke-WebRequest -Uri "https://github.com/oyyd/frozen_east_text_detection.pb/raw/master/frozen_east_text_detection.pb" -OutFile "$MODELS_DIR\east_text_detection.pb" - + # Download super resolution model Write-Host "Downloading ESPCN super resolution model..." Invoke-WebRequest -Uri "https://github.com/fannymonori/TF-ESPCN/raw/master/export/ESPCN_x4.pb" -OutFile "$MODELS_DIR\ESPCN_x4.pb" - + # Download English dictionary for spell checking Write-Host "Downloading English dictionary for spell checking..." Invoke-WebRequest -Uri "https://raw.githubusercontent.com/dwyl/english-words/master/words.txt" -OutFile "$DICT_DIR\english.txt" - + if ((Test-Path "$MODELS_DIR\east_text_detection.pb") -and (Test-Path "$MODELS_DIR\ESPCN_x4.pb")) { Write-Host "Models downloaded successfully" -ForegroundColor Green } else { @@ -461,22 +461,22 @@ function Install-Chocolatey { function Install-Vcpkg { if (-not (Test-Path $VCPKG_DIR)) { Write-Host "Installing vcpkg..." - + # Clone vcpkg repository git clone https://github.com/Microsoft/vcpkg.git $VCPKG_DIR - + # Run bootstrap script & "$VCPKG_DIR\bootstrap-vcpkg.bat" -disableMetrics - + # Add vcpkg to PATH $env:Path += ";$VCPKG_DIR" [Environment]::SetEnvironmentVariable("Path", $env:Path, [EnvironmentVariableTarget]::User) - + # Integrate vcpkg with Visual Studio & "$VCPKG_DIR\vcpkg" integrate install } else { Write-Host "vcpkg is already installed" - + # Update vcpkg Push-Location $VCPKG_DIR git pull @@ -499,40 +499,40 @@ function Install-BuildTools { # Install dependencies using vcpkg function Install-Dependencies { Write-Host "Installing dependencies using vcpkg..." - + # Install OpenCV & "$VCPKG_DIR\vcpkg" install opencv:x64-windows - + # Install Tesseract OCR & "$VCPKG_DIR\vcpkg" install tesseract:x64-windows - + # Install additional dependencies & "$VCPKG_DIR\vcpkg" install leptonica:x64-windows - + Write-Host "Dependencies installed successfully" -ForegroundColor Green } # Install additional tools function Install-AdditionalTools { Write-Host "Installing additional tools..." - + # Install Git if not already installed if (-not (Get-Command git -ErrorAction SilentlyContinue)) { choco install git -y } - + # Install CMake if not already installed if (-not (Get-Command cmake -ErrorAction SilentlyContinue)) { choco install cmake --installargs 'ADD_CMAKE_TO_PATH=System' -y } - + Write-Host "Additional tools installed successfully" -ForegroundColor Green } # Configure environment function Configure-Environment { Write-Host "Configuring environment..." - + # Create a sample config file $configJson = @" { @@ -574,16 +574,16 @@ function Configure-Environment { } } "@ - + Set-Content -Path "ocr_config.json" -Value $configJson - + Write-Host "Environment configured successfully" -ForegroundColor Green } # Create example compilation script function Create-CompilationScript { Write-Host "Creating compilation script..." - + $compileBat = @" @echo off REM Compile Enhanced OCR system @@ -605,49 +605,49 @@ cd .. echo Build completed. Check the 'build' directory for output. "@ - + Set-Content -Path "compile.bat" -Value $compileBat - + Write-Host "Compilation script created successfully" -ForegroundColor Green } # Main function function Main { Write-Host "Starting OCR dependencies installation for Windows..." -ForegroundColor Cyan - + # Create directories Create-Directories - + # Check if only downloading models if ($args[0] -eq "--models-only") { Download-Models return } - + # Install Chocolatey Install-Chocolatey - + # Install additional tools Install-AdditionalTools - + # Install Visual Studio Build Tools Install-BuildTools - + # Install vcpkg Install-Vcpkg - + # Install dependencies Install-Dependencies - + # Download models Download-Models - + # Configure environment Configure-Environment - + # Create compilation script Create-CompilationScript - + Write-Host "Installation completed successfully!" -ForegroundColor Green Write-Host "You can now build the Enhanced OCR system using the generated compile.bat script." } @@ -661,7 +661,7 @@ if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdenti # Run main function with passed arguments Main $args EOF - + success "Windows installation script created: Install-OCRDependencies.ps1" log "Please run this script on Windows with administrator privileges." } @@ -669,20 +669,20 @@ EOF # Main function main() { log "Starting OCR dependencies installation..." - + # Create directories create_directories - + # Check if only downloading models if [[ "$1" == "--models-only" ]]; then download_models success "Models downloaded successfully. Exiting." exit 0 fi - + # Detect OS detect_os - + # Install dependencies based on OS case $OS in debian) @@ -726,10 +726,10 @@ EOF exit 1 ;; esac - + # Download models download_models - + # Create sample config file log "Creating sample configuration file..." cat > ocr_config.json << EOF @@ -772,7 +772,7 @@ EOF } } EOF - + # Create CMakeLists.txt file log "Creating CMakeLists.txt file..." cat > CMakeLists.txt << EOF @@ -829,7 +829,7 @@ file(MAKE_DIRECTORY \${CMAKE_BINARY_DIR}/.ocr_cache) # Create logs directory in build directory file(MAKE_DIRECTORY \${CMAKE_BINARY_DIR}/logs) EOF - + # Create compilation script log "Creating compilation script..." cat > compile.sh << EOF @@ -854,10 +854,10 @@ cd .. echo "Build completed. Check the 'build' directory for output." EOF chmod +x compile.sh - + success "Installation completed successfully!" log "You can now build the Enhanced OCR system using the generated compile.sh script." } # Run main function with all arguments -main "$@" \ No newline at end of file +main "$@" diff --git a/atom/image/ocr/ocr.cpp b/atom/image/ocr/ocr.cpp index 532e053b..c6edda61 100644 --- a/atom/image/ocr/ocr.cpp +++ b/atom/image/ocr/ocr.cpp @@ -1504,4 +1504,4 @@ class EnhancedOCRProcessor { } } }; -}; \ No newline at end of file +}; diff --git a/atom/image/ocr/ocr.hpp b/atom/image/ocr/ocr.hpp index be39758a..9f410502 100644 --- a/atom/image/ocr/ocr.hpp +++ b/atom/image/ocr/ocr.hpp @@ -482,4 +482,4 @@ class EnhancedOCRProcessor { * @brief Clean up resources */ void cleanup(); -}; \ No newline at end of file +}; diff --git a/atom/image/ser/exception.h b/atom/image/ser/exception.h index b6c99efb..c4874924 100644 --- a/atom/image/ser/exception.h +++ b/atom/image/ser/exception.h @@ -75,4 +75,4 @@ class ResourceException : public SERException { : SERException(message, location) {} }; -} // namespace serastro \ No newline at end of file +} // namespace serastro diff --git a/atom/image/ser/frame_processor.cpp b/atom/image/ser/frame_processor.cpp index 28d0b352..6e907139 100644 --- a/atom/image/ser/frame_processor.cpp +++ b/atom/image/ser/frame_processor.cpp @@ -9,23 +9,23 @@ std::vector FrameProcessor::process(const std::vector& frames, const ProgressCallback& progress) { std::vector results; results.reserve(frames.size()); - + cancelRequested = false; - + for (size_t i = 0; i < frames.size(); ++i) { if (cancelRequested) { break; } - + results.push_back(process(frames[i])); - + if (progress) { double progressValue = static_cast(i + 1) / frames.size(); - progress(progressValue, std::format("{}: Processing frame {}/{}", + progress(progressValue, std::format("{}: Processing frame {}/{}", getName(), i + 1, frames.size())); } } - + return results; } @@ -84,37 +84,37 @@ ProcessingPipeline::ProcessingPipeline() = default; cv::Mat ProcessingPipeline::process(const cv::Mat& frame) { cv::Mat result = frame.clone(); - + for (auto& processor : processors) { if (cancelRequested) { break; } - + result = processor->process(result); } - + return result; } std::vector ProcessingPipeline::process(const std::vector& frames, const ProgressCallback& progress) { std::vector results = frames; - + cancelRequested = false; - + for (size_t i = 0; i < processors.size(); ++i) { if (cancelRequested) { break; } - + auto& processor = processors[i]; - + if (progress) { progress(static_cast(i) / processors.size(), - std::format("Running processor {}/{}: {}", + std::format("Running processor {}/{}: {}", i + 1, processors.size(), processor->getName())); } - + // Create a wrapper progress function that scales appropriately ProgressCallback processorProgress = nullptr; if (progress) { @@ -124,14 +124,14 @@ std::vector ProcessingPipeline::process(const std::vector& fra progress(overallProgress, message); }; } - + results = processor->process(results, processorProgress); - + if (processor->isCancelled()) { cancelRequested = true; } } - + return results; } @@ -161,4 +161,4 @@ void ProcessingPipeline::clear() { processors.clear(); } -} // namespace serastro \ No newline at end of file +} // namespace serastro diff --git a/atom/image/ser/frame_processor.h b/atom/image/ser/frame_processor.h index a4194e12..6dc1ef9e 100644 --- a/atom/image/ser/frame_processor.h +++ b/atom/image/ser/frame_processor.h @@ -20,17 +20,17 @@ using ProgressCallback = std::function process(const std::vector& frames, const ProgressCallback& progress = nullptr); - + // Get processor name virtual std::string getName() const = 0; - + // Allow cancellation of multi-frame processing void requestCancel() { cancelRequested = true; } bool isCancelled() const { return cancelRequested; } @@ -45,19 +45,19 @@ class CustomizableProcessor : public FrameProcessor { public: // Set parameter by name virtual void setParameter(const std::string& name, double value) = 0; - + // Get parameter value virtual double getParameter(const std::string& name) const = 0; - + // Get all parameter names virtual std::vector getParameterNames() const = 0; - + // Check if parameter exists virtual bool hasParameter(const std::string& name) const = 0; - + // Set multiple parameters virtual void setParameters(const std::unordered_map& params); - + // Get all parameters as a map virtual std::unordered_map getParameters() const; }; @@ -69,10 +69,10 @@ class BaseCustomizableProcessor : public CustomizableProcessor { double getParameter(const std::string& name) const override; std::vector getParameterNames() const override; bool hasParameter(const std::string& name) const override; - + protected: std::unordered_map parameters; - + // Register a parameter with initial value void registerParameter(const std::string& name, double initialValue); }; @@ -81,21 +81,21 @@ class BaseCustomizableProcessor : public CustomizableProcessor { class ProcessingPipeline : public FrameProcessor { public: ProcessingPipeline(); - + cv::Mat process(const cv::Mat& frame) override; std::vector process(const std::vector& frames, const ProgressCallback& progress = nullptr) override; std::string getName() const override; - + // Add processor to the pipeline void addProcessor(std::shared_ptr processor); - + // Remove processor by index void removeProcessor(size_t index); - + // Get all processors std::vector> getProcessors() const; - + // Clear all processors void clear(); @@ -103,4 +103,4 @@ class ProcessingPipeline : public FrameProcessor { std::vector> processors; }; -} // namespace serastro \ No newline at end of file +} // namespace serastro diff --git a/atom/image/ser/quality.cpp b/atom/image/ser/quality.cpp index 39675595..f7c2df18 100644 --- a/atom/image/ser/quality.cpp +++ b/atom/image/ser/quality.cpp @@ -37,27 +37,27 @@ double QualityAssessor::assessQuality(const cv::Mat& frame) const { std::vector QualityAssessor::getQualityScores(const std::vector& frames) const { std::vector scores; scores.reserve(frames.size()); - + for (const auto& frame : frames) { scores.push_back(assessQuality(frame)); } - + return scores; } std::vector QualityAssessor::sortFramesByQuality(const std::vector& frames) const { // Calculate quality scores std::vector scores = getQualityScores(frames); - + // Create index vector std::vector indices(frames.size()); std::iota(indices.begin(), indices.end(), 0); - + // Sort indices by scores (descending order) std::sort(indices.begin(), indices.end(), [&scores](size_t a, size_t b) { return scores[a] > scores[b]; }); - + return indices; } @@ -65,31 +65,31 @@ std::vector QualityAssessor::selectBestFrames(const std::vector bestFrames; bestFrames.reserve(count); - + for (size_t i = 0; i < count; ++i) { bestFrames.push_back(frames[sortedIndices[i]]); } - + return bestFrames; } -void QualityAssessor::addCustomMetric(const std::string& name, +void QualityAssessor::addCustomMetric(const std::string& name, QualityMetricFunction metricFunction, double weight) { if (weight <= 0.0) { throw InvalidParameterException("Metric weight must be greater than zero"); } - + customMetrics[name] = std::make_pair(std::move(metricFunction), weight); } @@ -134,20 +134,20 @@ double QualityAssessor::getCustomMetricValue(const cv::Mat& frame, const std::st if (it == customMetrics.end()) { throw InvalidParameterException(std::format("Unknown custom metric: {}", metricName)); } - + return it->second.first(frame); } std::vector QualityAssessor::getDetailedMetrics(const cv::Mat& frame) const { std::vector details; - + // Add standard metrics struct StdMetric { QualityMetric metric; std::string name; double weight; }; - + std::vector stdMetrics = { {QualityMetric::Sharpness, "Sharpness", parameters.metricWeights[0]}, {QualityMetric::SNR, "SNR", parameters.metricWeights[1]}, @@ -156,17 +156,17 @@ std::vector QualityAssessor::getDetailedMetrics( {QualityMetric::Contrast, "Contrast", parameters.metricWeights[4]}, {QualityMetric::StarCount, "StarCount", parameters.metricWeights[5]} }; - + // Calculate raw values std::vector rawValues; rawValues.reserve(stdMetrics.size() + customMetrics.size()); - + for (const auto& metric : stdMetrics) { double value = getMetricValue(frame, metric.metric); rawValues.push_back(value); details.push_back({metric.name, value, 0.0, metric.weight}); } - + // Add custom metrics for (const auto& [name, metricPair] : customMetrics) { const auto& [metricFunc, weight] = metricPair; @@ -174,7 +174,7 @@ std::vector QualityAssessor::getDetailedMetrics( rawValues.push_back(value); details.push_back({name, value, 0.0, weight}); } - + // Normalize if requested if (parameters.normalizeMetrics) { // Find min and max for each metric @@ -190,7 +190,7 @@ std::vector QualityAssessor::getDetailedMetrics( details[i].normalizedValue = details[i].rawValue; } } - + return details; } @@ -198,10 +198,10 @@ cv::Rect QualityAssessor::calculateROI(const cv::Mat& frame) const { // Calculate ROI based on selected method int width = frame.cols; int height = frame.rows; - + int roiWidth = static_cast(width * parameters.roiSize); int roiHeight = static_cast(height * parameters.roiSize); - + if (parameters.roiSelector == "centered") { // Centered ROI int x = (width - roiWidth) / 2; @@ -211,13 +211,13 @@ cv::Rect QualityAssessor::calculateROI(const cv::Mat& frame) const { // Find brightest region (simplified) cv::Mat blurred; cv::GaussianBlur(frame, blurred, cv::Size(21, 21), 5); - + cv::Point maxLoc; cv::minMaxLoc(blurred, nullptr, nullptr, nullptr, &maxLoc); - + int x = std::clamp(maxLoc.x - roiWidth/2, 0, width - roiWidth); int y = std::clamp(maxLoc.y - roiHeight/2, 0, height - roiHeight); - + return cv::Rect(x, y, roiWidth, roiHeight); } else { // Default to full frame @@ -233,7 +233,7 @@ double QualityAssessor::calculateSharpness(const cv::Mat& frame) const { } else { gray = frame; } - + // Convert to float if needed cv::Mat floatImg; if (gray.depth() != CV_32F) { @@ -241,21 +241,21 @@ double QualityAssessor::calculateSharpness(const cv::Mat& frame) const { } else { floatImg = gray; } - + // Calculate ROI cv::Rect roi = calculateROI(floatImg); cv::Mat roiImg = floatImg(roi); - + // Apply Laplacian cv::Mat laplacian; cv::Laplacian(roiImg, laplacian, CV_32F, 3); - + // Calculate variance of Laplacian (measure of sharpness) cv::Scalar mean, stddev; cv::meanStdDev(laplacian, mean, stddev); - + double variance = stddev[0] * stddev[0]; - + // Normalize to a reasonable range (empirical) return std::min(variance / 100.0, 1.0); } @@ -268,30 +268,30 @@ double QualityAssessor::calculateSNR(const cv::Mat& frame) const { } else { gray = frame; } - + // Convert to float cv::Mat floatImg; gray.convertTo(floatImg, CV_32F); - + // Calculate ROI cv::Rect roi = calculateROI(floatImg); cv::Mat roiImg = floatImg(roi); - + // Apply Gaussian blur to estimate signal cv::Mat blurred; cv::GaussianBlur(roiImg, blurred, cv::Size(0, 0), 3); - + // Estimate noise as difference between original and blurred cv::Mat noise = roiImg - blurred; - + // Calculate statistics cv::Scalar signalMean, signalStdDev, noiseStdDev; cv::meanStdDev(blurred, signalMean, signalStdDev); cv::meanStdDev(noise, cv::Scalar(), noiseStdDev); - + // SNR = signal / noise double snr = signalMean[0] / (noiseStdDev[0] + 1e-6); - + // Normalize to a reasonable range (empirical) return std::min(snr / 20.0, 1.0); } @@ -304,7 +304,7 @@ double QualityAssessor::calculateEntropy(const cv::Mat& frame) const { } else { gray = frame; } - + // Ensure 8-bit for histogram cv::Mat img8bit; if (gray.depth() != CV_8U) { @@ -312,22 +312,22 @@ double QualityAssessor::calculateEntropy(const cv::Mat& frame) const { } else { img8bit = gray; } - + // Calculate ROI cv::Rect roi = calculateROI(img8bit); cv::Mat roiImg = img8bit(roi); - + // Calculate histogram cv::Mat hist; int histSize = 256; float range[] = {0, 256}; const float* histRange = {range}; cv::calcHist(&roiImg, 1, 0, cv::Mat(), hist, 1, &histSize, &histRange); - + // Normalize histogram double pixelCount = roiImg.total(); hist /= pixelCount; - + // Calculate entropy double entropy = 0.0; for (int i = 0; i < histSize; i++) { @@ -336,7 +336,7 @@ double QualityAssessor::calculateEntropy(const cv::Mat& frame) const { entropy -= binVal * std::log2(binVal); } } - + // Normalize to 0-1 range (max entropy for 8-bit is 8) return std::min(entropy / 8.0, 1.0); } @@ -349,14 +349,14 @@ double QualityAssessor::calculateBrightness(const cv::Mat& frame) const { } else { gray = frame; } - + // Calculate ROI cv::Rect roi = calculateROI(gray); cv::Mat roiImg = gray(roi); - + // Calculate mean brightness cv::Scalar meanVal = cv::mean(roiImg); - + // Normalize based on bit depth double normFactor = 1.0; if (gray.depth() == CV_8U) { @@ -364,7 +364,7 @@ double QualityAssessor::calculateBrightness(const cv::Mat& frame) const { } else if (gray.depth() == CV_16U) { normFactor = 65535.0; } - + return meanVal[0] / normFactor; } @@ -376,19 +376,19 @@ double QualityAssessor::calculateContrast(const cv::Mat& frame) const { } else { gray = frame; } - + // Convert to float cv::Mat floatImg; gray.convertTo(floatImg, CV_32F); - + // Calculate ROI cv::Rect roi = calculateROI(floatImg); cv::Mat roiImg = floatImg(roi); - + // Calculate standard deviation (measure of contrast) cv::Scalar mean, stddev; cv::meanStdDev(roiImg, mean, stddev); - + // Normalize by maximum possible standard deviation double maxStdDev = 0.5; // For normalized [0,1] image if (gray.depth() == CV_8U) { @@ -396,7 +396,7 @@ double QualityAssessor::calculateContrast(const cv::Mat& frame) const { } else if (gray.depth() == CV_16U) { maxStdDev = 32767.5; } - + return std::min(stddev[0] / maxStdDev, 1.0); } @@ -408,7 +408,7 @@ double QualityAssessor::calculateStarCount(const cv::Mat& frame) const { } else { gray = frame; } - + // Ensure 8-bit for blob detection cv::Mat img8bit; if (gray.depth() != CV_8U) { @@ -416,37 +416,37 @@ double QualityAssessor::calculateStarCount(const cv::Mat& frame) const { } else { img8bit = gray; } - + // Calculate ROI cv::Rect roi = calculateROI(img8bit); cv::Mat roiImg = img8bit(roi); - + // Threshold the image to find bright points cv::Mat thresholded; double thresh = parameters.starDetectionThreshold * 255.0; cv::threshold(roiImg, thresholded, thresh, 255, cv::THRESH_BINARY); - + // Find contours std::vector> contours; cv::findContours(thresholded, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); - + // Filter contours by size and shape to find star-like objects int starCount = 0; for (const auto& contour : contours) { double area = cv::contourArea(contour); - + // Stars are typically small and roughly circular if (area > 3 && area < 100) { // Check circularity double perimeter = cv::arcLength(contour, true); double circularity = 4 * M_PI * area / (perimeter * perimeter); - + if (circularity > 0.7) { // More circular than not starCount++; } } } - + // Normalize star count to 0-1 (assuming max ~100 stars in frame) return std::min(static_cast(starCount) / 100.0, 1.0); } @@ -459,11 +459,11 @@ double QualityAssessor::calculateCompositeScore(const cv::Mat& frame) const { double brightness = calculateBrightness(frame); double contrast = calculateContrast(frame); double starCount = calculateStarCount(frame); - + // Calculate weighted sum double weightSum = 0; double score = 0; - + // Standard metrics const std::vector values = {sharpness, snr, entropy, brightness, contrast, starCount}; for (size_t i = 0; i < values.size(); ++i) { @@ -472,7 +472,7 @@ double QualityAssessor::calculateCompositeScore(const cv::Mat& frame) const { weightSum += parameters.metricWeights[i]; } } - + // Add custom metrics for (const auto& [name, metricPair] : customMetrics) { const auto& [metricFunc, weight] = metricPair; @@ -480,13 +480,13 @@ double QualityAssessor::calculateCompositeScore(const cv::Mat& frame) const { score += value * weight; weightSum += weight; } - + // Normalize by sum of weights if (weightSum > 0) { score /= weightSum; } - + return score; } -} // namespace serastro \ No newline at end of file +} // namespace serastro diff --git a/atom/image/ser/quality.h b/atom/image/ser/quality.h index e49cb7ae..f7e4a7b0 100644 --- a/atom/image/ser/quality.h +++ b/atom/image/ser/quality.h @@ -42,35 +42,35 @@ class QualityAssessor { public: QualityAssessor(); explicit QualityAssessor(const QualityParameters& params); - + // Assess quality of a single frame double assessQuality(const cv::Mat& frame) const; - + // Get quality scores as vector std::vector getQualityScores(const std::vector& frames) const; - + // Sort frames by quality (returns indices of frames in descending order) std::vector sortFramesByQuality(const std::vector& frames) const; - + // Select best N frames std::vector selectBestFrames(const std::vector& frames, size_t count) const; - + // Add custom quality metric - void addCustomMetric(const std::string& name, + void addCustomMetric(const std::string& name, QualityMetricFunction metricFunction, double weight = 1.0); - + // Remove custom metric void removeCustomMetric(const std::string& name); - + // Get/set parameters void setParameters(const QualityParameters& params); const QualityParameters& getParameters() const; - + // Get value of specific metric double getMetricValue(const cv::Mat& frame, QualityMetric metric) const; double getCustomMetricValue(const cv::Mat& frame, const std::string& metricName) const; - + // Get details of all metrics for a frame struct MetricDetails { std::string name; @@ -78,16 +78,16 @@ class QualityAssessor { double normalizedValue; double weight; }; - + std::vector getDetailedMetrics(const cv::Mat& frame) const; private: QualityParameters parameters; std::unordered_map> customMetrics; - + // Calculate ROI for quality assessment cv::Rect calculateROI(const cv::Mat& frame) const; - + // Internal implementations for standard metrics double calculateSharpness(const cv::Mat& frame) const; double calculateSNR(const cv::Mat& frame) const; @@ -98,4 +98,4 @@ class QualityAssessor { double calculateCompositeScore(const cv::Mat& frame) const; }; -} // namespace serastro \ No newline at end of file +} // namespace serastro diff --git a/atom/image/ser/registration.h b/atom/image/ser/registration.h index f131c075..f098aa29 100644 --- a/atom/image/ser/registration.h +++ b/atom/image/ser/registration.h @@ -49,14 +49,14 @@ struct FrameTransformation { Perspective, // Perspective transform Polynomial // Higher-order polynomial transform }; - + Type type = Type::Translation; cv::Mat transform; // Transformation matrix double confidence = 0.0; // Confidence score (0-1) - + // Apply transformation to a point cv::Point2f apply(const cv::Point2f& pt) const; - + // Apply transformation to a frame cv::Mat applyToFrame(const cv::Mat& frame, const cv::Size& outputSize = cv::Size()) const; }; @@ -66,32 +66,32 @@ class FrameRegistrar : public CustomizableProcessor { public: FrameRegistrar(); explicit FrameRegistrar(const RegistrationParameters& params); - + // Calculate transformation between frames FrameTransformation calculateTransformation(const cv::Mat& frame) const; - + // Register frame and return transformation std::pair registerFrame(const cv::Mat& frame) const; - + // Register and apply in one step cv::Mat registerAndApply(const cv::Mat& frame); - + // Set reference frame void setReferenceFrame(const cv::Mat& referenceFrame); - + // Auto-select reference frame from a set of frames void autoSelectReferenceFrame(const std::vector& frames); - + // Get reference frame cv::Mat getReferenceFrame() const; - + // Check if reference frame is set bool hasReferenceFrame() const; - + // Register multiple frames std::vector registerFrames(const std::vector& frames, const ProgressCallback& progress = nullptr); - + // CustomizableProcessor interface implementation cv::Mat process(const cv::Mat& frame) override; std::string getName() const override; @@ -99,11 +99,11 @@ class FrameRegistrar : public CustomizableProcessor { double getParameter(const std::string& name) const override; std::vector getParameterNames() const override; bool hasParameter(const std::string& name) const override; - + // Set/get registration parameters void setRegistrationParameters(const RegistrationParameters& params); const RegistrationParameters& getRegistrationParameters() const; - + // Set quality assessor for reference frame selection void setQualityAssessor(std::shared_ptr assessor); std::shared_ptr getQualityAssessor() const; @@ -113,18 +113,18 @@ class FrameRegistrar : public CustomizableProcessor { cv::Mat referenceFrame; bool hasReference = false; std::shared_ptr qualityAssessor; - + // Transformation methods FrameTransformation calculatePhaseCorrelation(const cv::Mat& frame) const; FrameTransformation calculateFeatureMatching(const cv::Mat& frame) const; FrameTransformation calculateOpticalFlow(const cv::Mat& frame) const; FrameTransformation calculateECC(const cv::Mat& frame) const; FrameTransformation calculateTemplateMatching(const cv::Mat& frame) const; - + // Helper methods cv::Mat prepareFrameForRegistration(const cv::Mat& frame) const; cv::Rect calculateCommonArea(const std::vector& transforms, const cv::Size& frameSize) const; }; -} // namespace serastro \ No newline at end of file +} // namespace serastro diff --git a/atom/image/ser/ser.hpp b/atom/image/ser/ser.hpp index e4cd1726..5bf28379 100644 --- a/atom/image/ser/ser.hpp +++ b/atom/image/ser/ser.hpp @@ -40,4 +40,4 @@ struct LibraryInfo { } }; -} // namespace serastro \ No newline at end of file +} // namespace serastro diff --git a/atom/image/ser/ser_format.h b/atom/image/ser/ser_format.h index 63a465bd..fae19218 100644 --- a/atom/image/ser/ser_format.h +++ b/atom/image/ser/ser_format.h @@ -192,4 +192,4 @@ struct SERHeader { } }; -} // namespace serastro \ No newline at end of file +} // namespace serastro diff --git a/atom/image/ser/ser_reader.cpp b/atom/image/ser/ser_reader.cpp index 1e9dcc7b..1ac18b31 100644 --- a/atom/image/ser/ser_reader.cpp +++ b/atom/image/ser/ser_reader.cpp @@ -407,4 +407,4 @@ void SERReader::clearCache() const { pImpl->currentCacheSize = 0; } -} // namespace serastro \ No newline at end of file +} // namespace serastro diff --git a/atom/image/ser/ser_reader.h b/atom/image/ser/ser_reader.h index 2e2d30a4..7472461f 100644 --- a/atom/image/ser/ser_reader.h +++ b/atom/image/ser/ser_reader.h @@ -105,4 +105,4 @@ class SERReader { std::unique_ptr pImpl; }; -} // namespace serastro \ No newline at end of file +} // namespace serastro diff --git a/atom/image/ser/ser_writer.cpp b/atom/image/ser/ser_writer.cpp index f9af304f..92710136 100644 --- a/atom/image/ser/ser_writer.cpp +++ b/atom/image/ser/ser_writer.cpp @@ -245,4 +245,4 @@ void SERWriter::finalize() { // Get current number of frames written size_t SERWriter::getFrameCount() const { return pImpl->currentFrameCount; } -} // namespace serastro \ No newline at end of file +} // namespace serastro diff --git a/atom/image/ser/ser_writer.h b/atom/image/ser/ser_writer.h index 0970d744..0cef1980 100644 --- a/atom/image/ser/ser_writer.h +++ b/atom/image/ser/ser_writer.h @@ -25,26 +25,26 @@ class SERWriter { public: // Create a new SER file explicit SERWriter(const std::filesystem::path& filePath, const SERHeader& header); - + // Destructor ~SERWriter(); - + // Write a frame to the file void writeFrame(const cv::Mat& frame, const WriteOptions& options = {}); - + // Write a frame with a timestamp void writeFrameWithTimestamp(const cv::Mat& frame, uint64_t timestamp, const WriteOptions& options = {}); - + // Write multiple frames void writeFrames(const std::vector& frames, const WriteOptions& options = {}); - + // Finalize the file (updates header with frame count) void finalize(); - + // Get current number of frames written size_t getFrameCount() const; - + // Write custom raw frame data (advanced) void writeRawFrame(const std::vector& frameData); @@ -53,4 +53,4 @@ class SERWriter { std::unique_ptr pImpl; }; -} // namespace serastro \ No newline at end of file +} // namespace serastro diff --git a/atom/image/ser/stacking.h b/atom/image/ser/stacking.h index a35517a2..88bf0473 100644 --- a/atom/image/ser/stacking.h +++ b/atom/image/ser/stacking.h @@ -29,10 +29,10 @@ enum class StackingMethod { class FrameWeightCalculator { public: virtual ~FrameWeightCalculator() = default; - + // Calculate weight for a single frame virtual double calculateWeight(const cv::Mat& frame) = 0; - + // Calculate weights for multiple frames virtual std::vector calculateWeights(const std::vector& frames); }; @@ -41,10 +41,10 @@ class FrameWeightCalculator { class QualityWeightCalculator : public FrameWeightCalculator { public: explicit QualityWeightCalculator(std::shared_ptr assessor = nullptr); - + double calculateWeight(const cv::Mat& frame) override; std::vector calculateWeights(const std::vector& frames) override; - + void setQualityAssessor(std::shared_ptr assessor); std::shared_ptr getQualityAssessor() const; @@ -72,14 +72,14 @@ class FrameStacker : public CustomizableProcessor { public: FrameStacker(); explicit FrameStacker(const StackingParameters& params); - + // Stack multiple frames cv::Mat stackFrames(const std::vector& frames); - + // Stack with explicit weights - cv::Mat stackFramesWithWeights(const std::vector& frames, + cv::Mat stackFramesWithWeights(const std::vector& frames, const std::vector& weights); - + // CustomizableProcessor interface implementation cv::Mat process(const cv::Mat& frame) override; std::string getName() const override; @@ -87,15 +87,15 @@ class FrameStacker : public CustomizableProcessor { double getParameter(const std::string& name) const override; std::vector getParameterNames() const override; bool hasParameter(const std::string& name) const override; - + // Set/get stacking parameters void setStackingParameters(const StackingParameters& params); const StackingParameters& getStackingParameters() const; - + // Set/get weight calculator void setWeightCalculator(std::shared_ptr calculator); std::shared_ptr getWeightCalculator() const; - + // Buffer management void addFrameToBuffer(const cv::Mat& frame); void clearBuffer(); @@ -107,7 +107,7 @@ class FrameStacker : public CustomizableProcessor { StackingParameters parameters; std::vector frameBuffer; size_t maxBufferSize = 100; - + // Implementation methods for different stacking algorithms cv::Mat stackMean(const std::vector& frames) const; cv::Mat stackMedian(const std::vector& frames) const; @@ -116,12 +116,12 @@ class FrameStacker : public CustomizableProcessor { cv::Mat stackSigmaClipping(const std::vector& frames) const; cv::Mat stackWeightedAverage(const std::vector& frames, const std::vector& weights) const; - + // Prepare frames for stacking (convert to float, normalize, etc.) std::vector prepareFrames(const std::vector& frames) const; - + // Normalize result after stacking cv::Mat normalizeResult(const cv::Mat& stacked) const; }; -} // namespace serastro \ No newline at end of file +} // namespace serastro diff --git a/atom/image/ser/utils.cpp b/atom/image/ser/utils.cpp index 33ad02fd..4ef112a3 100644 --- a/atom/image/ser/utils.cpp +++ b/atom/image/ser/utils.cpp @@ -651,4 +651,4 @@ std::string getLibraryVersion() { std::string getOpenCVVersion() { return CV_VERSION; } } // namespace utils -} // namespace serastro \ No newline at end of file +} // namespace serastro diff --git a/atom/image/ser/utils.h b/atom/image/ser/utils.h index b4b96a19..8682534b 100644 --- a/atom/image/ser/utils.h +++ b/atom/image/ser/utils.h @@ -25,11 +25,11 @@ cv::Mat convertToRGB(const cv::Mat& src); // Normalization cv::Mat normalize(const cv::Mat& src, double alpha = 0.0, double beta = 1.0); cv::Mat normalizeMinMax(const cv::Mat& src); -cv::Mat normalizePercentile(const cv::Mat& src, double lowPercentile = 0.5, +cv::Mat normalizePercentile(const cv::Mat& src, double lowPercentile = 0.5, double highPercentile = 99.5); // File utilities -std::vector findSerFiles(const std::filesystem::path& directory, +std::vector findSerFiles(const std::filesystem::path& directory, bool recursive = false); std::optional estimateFrameCount(const std::filesystem::path& serFile); bool isValidSerFile(const std::filesystem::path& serFile); @@ -62,7 +62,7 @@ std::vector detectHotPixels(const cv::Mat& image, double threshold = std::vector detectColdPixels(const cv::Mat& image, double threshold = 0.05); // Create bad pixel map -cv::Mat createBadPixelMask(const cv::Mat& image, double hotThreshold = 0.95, +cv::Mat createBadPixelMask(const cv::Mat& image, double hotThreshold = 0.95, double coldThreshold = 0.05); // Fix bad pixels @@ -76,4 +76,4 @@ std::string getLibraryVersion(); std::string getOpenCVVersion(); } // namespace utils -} // namespace serastro \ No newline at end of file +} // namespace serastro diff --git a/atom/image/xmake.lua b/atom/image/xmake.lua index ea84f10f..605802c3 100644 --- a/atom/image/xmake.lua +++ b/atom/image/xmake.lua @@ -38,24 +38,24 @@ add_requires("cfitsio", {optional = true}) -- Object Library target("atom-image-object") set_kind("object") - + -- Add files add_files(table.unpack(source_files)) add_headerfiles(table.unpack(header_files)) - + -- Add dependencies add_packages("loguru") - + -- Add optional dependency on cfitsio if available if has_package("cfitsio") then add_packages("cfitsio") add_defines("HAS_CFITSIO") end - + -- Add include directories add_includedirs(".", {public = true}) add_includedirs("..", {public = true}) - + -- Set C++ standard set_languages("c++20") target_end() @@ -64,20 +64,20 @@ target_end() target("atom-image") -- Set library type based on parent project option set_kind(has_config("shared_libs") and "shared" or "static") - + -- Add dependencies add_deps("atom-image-object") add_packages("loguru") - + -- Add optional dependency on cfitsio if available if has_package("cfitsio") then add_packages("cfitsio") end - + -- Set output directories set_targetdir("$(buildir)/lib") set_objectdir("$(buildir)/obj") - + -- Install configuration on_install(function (target) os.cp(target:targetfile(), path.join(target:installdir(), "lib")) diff --git a/atom/io/compress.cpp b/atom/io/compress.cpp index fca62cc4..ef9c1ad6 100644 --- a/atom/io/compress.cpp +++ b/atom/io/compress.cpp @@ -82,7 +82,7 @@ class ZStreamGuard { // Initialize for compression bool initDeflate(int level, int windowBits = 7) { - int ret = deflateInit2(&stream_, level, Z_DEFLATED, windowBits, + int ret = deflateInit2(&stream_, level, Z_DEFLATED, windowBits, 8, Z_DEFAULT_STRATEGY); if (ret == Z_OK) { initialized_ = true; @@ -2064,27 +2064,27 @@ std::pair> compressData( zs.next_in = const_cast(reinterpret_cast(data_ptr)); zs.avail_out = static_cast(compressed_bound); zs.next_out = reinterpret_cast(compressed_data.data()); - + // Initialize deflate with window_bits from options - int ret = deflateInit2(&zs, options.level, Z_DEFLATED, options.window_bits, + int ret = deflateInit2(&zs, options.level, Z_DEFLATED, options.window_bits, 8, Z_DEFAULT_STRATEGY); if (ret != Z_OK) { compression_result.error_message = getZlibErrorMessage(ret); return result_pair; } - + // Use RAII for zstream cleanup std::unique_ptr deflate_guard(&zs, deflateEnd); - + // Perform compression in one step ret = deflate(&zs, Z_FINISH); - + if (ret != Z_STREAM_END) { - compression_result.error_message = + compression_result.error_message = String("Compression failed: ") + getZlibErrorMessage(ret); return result_pair; } - + // Use actual bytes written uLongf actual_compressed_size = zs.total_out; @@ -2156,7 +2156,7 @@ std::pair> decompressData( // Try to detect compression type from header bytes for better buffer estimation if (compressed_data_size >= 2) { const unsigned char* header = reinterpret_cast(compressed_data_ptr); - + // Check for gzip magic signature (0x1F, 0x8B) if (header[0] == 0x1F && header[1] == 0x8B) { // Gzip typically has 2:1 to 10:1 compression ratio @@ -2176,12 +2176,12 @@ std::pair> decompressData( buffer_size = 4096; } } - + // Ensure minimum buffer size if (buffer_size < 1024) { buffer_size = 1024; } - + decompressed_data.resize(buffer_size); // Use z_stream for more control, especially for potential resizing @@ -2199,7 +2199,7 @@ std::pair> decompressData( // For gzip/zlib auto-detection, add 32 (15+32) // For raw deflate with no header, use negative value (-15) int windowBits = options.window_bits; - + // Auto-detect based on header bytes if possible if (compressed_data_size >= 2) { const unsigned char* header = reinterpret_cast(compressed_data_ptr); @@ -2215,7 +2215,7 @@ std::pair> decompressData( } // If not recognized, use as-is (for raw deflate) } - + int ret = inflateInit2(&zs, windowBits); if (ret != Z_OK) { compression_result.error_message = getZlibErrorMessage(ret); @@ -2236,7 +2236,7 @@ std::pair> decompressData( if (zs.avail_out == 0) { // Buffer is full, resize it with an optimized growth strategy size_t old_size = decompressed_data.size(); - + // Smart growth strategy: // - For small buffers (<64KB): double the size // - For medium buffers (64KB-1MB): grow by 50% @@ -2250,14 +2250,14 @@ std::pair> decompressData( size_t increment = std::max(old_size / 4, size_t(1048576)); new_size = old_size + increment; } - + // Check for overflow if (new_size <= old_size) { compression_result.error_message = "Decompression buffer size overflow"; return result_pair; // inflate_guard handles cleanup } - + // Allocate new buffer try { decompressed_data.resize(new_size); @@ -2266,7 +2266,7 @@ std::pair> decompressData( "Memory allocation failed during decompression"; return result_pair; } - + // Update stream pointers after resize zs.avail_out = static_cast(decompressed_data.size() - zs.total_out); diff --git a/atom/io/file_permission.cpp b/atom/io/file_permission.cpp index 182d0e6f..84e9c5e9 100644 --- a/atom/io/file_permission.cpp +++ b/atom/io/file_permission.cpp @@ -425,4 +425,4 @@ void changeFilePermissions(const fs::path& filePath, } } -} // namespace atom::io \ No newline at end of file +} // namespace atom::io diff --git a/atom/io/file_permission.hpp b/atom/io/file_permission.hpp index 92aecdb4..06afb457 100644 --- a/atom/io/file_permission.hpp +++ b/atom/io/file_permission.hpp @@ -77,4 +77,4 @@ std::string getSelfPermissions() noexcept; void changeFilePermissions(const std::filesystem::path &filePath, const atom::containers::String &permissions); -} // namespace atom::io \ No newline at end of file +} // namespace atom::io diff --git a/atom/io/xmake.lua b/atom/io/xmake.lua index cf529e5c..1421de04 100644 --- a/atom/io/xmake.lua +++ b/atom/io/xmake.lua @@ -49,41 +49,41 @@ local headers = { target("atom-io") -- Set target kind to static library set_kind("static") - + -- Add source files add_files(sources) - + -- Add header files add_headerfiles(headers) - + -- Add include directories add_includedirs(".", {public = true}) - + -- Add packages add_packages("loguru", "minizip", "zlib", "tbb") - + -- Add system libraries add_syslinks("pthread") - + -- Windows-specific libraries if is_plat("windows") then add_syslinks("ws2_32", "wsock32") end - + -- Enable position independent code add_cxflags("-fPIC", {tools = {"gcc", "clang"}}) add_cflags("-fPIC", {tools = {"gcc", "clang"}}) - + -- Set version info set_version("1.0.0") - + -- Set output name set_basename("atom-io") - + -- Set target and object directories set_targetdir("$(buildir)/lib") set_objectdir("$(buildir)/obj") - + -- Installation rules after_install(function (target) local installdir = target:installdir() or "$(prefix)" @@ -100,21 +100,21 @@ target("atom-io") -- Optional: Create object library target (equivalent to CMake's object library) target("atom-io-object") set_kind("object") - + -- Add the same source files add_files(sources) add_headerfiles(headers) - + -- Configuration add_includedirs(".") add_packages("loguru", "minizip", "zlib", "tbb") add_syslinks("pthread") - + -- Windows-specific libraries if is_plat("windows") then add_syslinks("ws2_32", "wsock32") end - + -- Enable position independent code add_cxflags("-fPIC", {tools = {"gcc", "clang"}}) add_cflags("-fPIC", {tools = {"gcc", "clang"}}) diff --git a/atom/log/async_logger.cpp b/atom/log/async_logger.cpp index f610117c..5c60d059 100644 --- a/atom/log/async_logger.cpp +++ b/atom/log/async_logger.cpp @@ -854,4 +854,4 @@ Task AsyncLogger::logAsync(LogLevel level, std::string msg, co_return; } -} // namespace atom::log \ No newline at end of file +} // namespace atom::log diff --git a/atom/log/async_logger.hpp b/atom/log/async_logger.hpp index eaeaa14c..e49c6960 100644 --- a/atom/log/async_logger.hpp +++ b/atom/log/async_logger.hpp @@ -464,4 +464,4 @@ class AsyncLogger { } // namespace atom::log -#endif // ATOM_LOG_ASYNC_LOGGER_HPP \ No newline at end of file +#endif // ATOM_LOG_ASYNC_LOGGER_HPP diff --git a/atom/log/atomlog.cpp b/atom/log/atomlog.cpp index eb03637e..a14dd610 100644 --- a/atom/log/atomlog.cpp +++ b/atom/log/atomlog.cpp @@ -912,4 +912,4 @@ std::shared_ptr Logger::create(const fs::path& file_name) { return std::make_shared(file_name); } -} // namespace atom::log \ No newline at end of file +} // namespace atom::log diff --git a/atom/log/atomlog.hpp b/atom/log/atomlog.hpp index 111a3c00..452bc622 100644 --- a/atom/log/atomlog.hpp +++ b/atom/log/atomlog.hpp @@ -443,4 +443,4 @@ class Logger { } // namespace atom::log -#endif // ATOM_LOG_ATOMLOG_HPP \ No newline at end of file +#endif // ATOM_LOG_ATOMLOG_HPP diff --git a/atom/log/log_manager.cpp b/atom/log/log_manager.cpp index eec3d1a9..a42b2c9a 100644 --- a/atom/log/log_manager.cpp +++ b/atom/log/log_manager.cpp @@ -322,4 +322,4 @@ void LogManager::flushAll() { } } -} // namespace atom::log \ No newline at end of file +} // namespace atom::log diff --git a/atom/log/log_manager.hpp b/atom/log/log_manager.hpp index 5ee53d3c..0eb3db2a 100644 --- a/atom/log/log_manager.hpp +++ b/atom/log/log_manager.hpp @@ -255,4 +255,4 @@ inline std::optional> getMmapLogger( } // namespace atom::log -#endif // ATOM_LOG_LOG_MANAGER_HPP \ No newline at end of file +#endif // ATOM_LOG_LOG_MANAGER_HPP diff --git a/atom/log/mmap_logger.cpp b/atom/log/mmap_logger.cpp index d9278529..1c177515 100644 --- a/atom/log/mmap_logger.cpp +++ b/atom/log/mmap_logger.cpp @@ -1169,4 +1169,4 @@ void MmapLogger::log(LogLevel level, Category category, std::string_view msg, impl_->log(level, category, msg, location); } -} // namespace atom::log \ No newline at end of file +} // namespace atom::log diff --git a/atom/log/mmap_logger.hpp b/atom/log/mmap_logger.hpp index 47566d24..f92ac830 100644 --- a/atom/log/mmap_logger.hpp +++ b/atom/log/mmap_logger.hpp @@ -356,4 +356,4 @@ using LoggerConfig = MmapLogger::Config; } // namespace atom::log -#endif // ATOM_LOG_MMAP_LOGGER_HPP \ No newline at end of file +#endif // ATOM_LOG_MMAP_LOGGER_HPP diff --git a/atom/log/xmake.lua b/atom/log/xmake.lua index 1e3733b3..913b3e49 100644 --- a/atom/log/xmake.lua +++ b/atom/log/xmake.lua @@ -26,28 +26,28 @@ local headers = { -- Object Library target("atom-log-object") set_kind("object") - + -- Add files add_files(table.unpack(sources)) add_headerfiles(table.unpack(headers)) - + -- Add dependencies add_packages("loguru") - + -- Add include directories add_includedirs(".", {public = true}) add_includedirs("..", {public = true}) - + -- Set C++ standard set_languages("c++20") - + -- Configure loguru options if is_plat("windows") then add_defines("LOGURU_STACKTRACES=1", {public = true}) else add_defines("LOGURU_STACKTRACES=1", {public = true}) end - + add_defines("LOGURU_WITH_STREAMS=1", {public = true}) add_defines("LOGURU_RTTI=1", {public = true}) target_end() @@ -56,11 +56,11 @@ target_end() target("atom-log") -- Set library type based on parent project option set_kind(has_config("shared_libs") and "shared" or "static") - + -- Add dependencies add_deps("atom-log-object") add_packages("loguru") - + -- Platform-specific settings if is_plat("windows") then add_packages("dlfcn-win32") @@ -68,11 +68,11 @@ target("atom-log") else add_syslinks("dl", "pthread") end - + -- Set output directories set_targetdir("$(buildir)/lib") set_objectdir("$(buildir)/obj") - + -- Install configuration on_install(function (target) os.cp(target:targetfile(), path.join(target:installdir(), "lib")) diff --git a/atom/memory/memory_pool.hpp b/atom/memory/memory_pool.hpp index eebafca6..11e3d3ed 100644 --- a/atom/memory/memory_pool.hpp +++ b/atom/memory/memory_pool.hpp @@ -345,4 +345,4 @@ template } } // namespace memory -} // namespace atom \ No newline at end of file +} // namespace atom diff --git a/atom/memory/object.hpp b/atom/memory/object.hpp index abc50beb..30469a72 100644 --- a/atom/memory/object.hpp +++ b/atom/memory/object.hpp @@ -820,4 +820,4 @@ class ObjectPool { } // namespace atom::memory -#endif // ATOM_MEMORY_OBJECT_POOL_HPP \ No newline at end of file +#endif // ATOM_MEMORY_OBJECT_POOL_HPP diff --git a/atom/memory/ring.hpp b/atom/memory/ring.hpp index c44c6d3c..9823e67c 100644 --- a/atom/memory/ring.hpp +++ b/atom/memory/ring.hpp @@ -470,4 +470,4 @@ class RingBuffer { } // namespace atom::memory -#endif // ATOM_ALGORITHM_RING_HPP \ No newline at end of file +#endif // ATOM_ALGORITHM_RING_HPP diff --git a/atom/memory/shared.hpp b/atom/memory/shared.hpp index eb88087d..0a18e848 100644 --- a/atom/memory/shared.hpp +++ b/atom/memory/shared.hpp @@ -1206,4 +1206,4 @@ auto SharedMemory::getNativeHandle() const -> void* { } // namespace atom::connection -#endif // ATOM_CONNECTION_SHARED_MEMORY_HPP \ No newline at end of file +#endif // ATOM_CONNECTION_SHARED_MEMORY_HPP diff --git a/atom/memory/tracker.hpp b/atom/memory/tracker.hpp index a699033f..78135432 100644 --- a/atom/memory/tracker.hpp +++ b/atom/memory/tracker.hpp @@ -544,4 +544,4 @@ void operator delete[](void* ptr, const std::nothrow_t&) noexcept { #endif // ATOM_MEMORY_TRACKING_ENABLED -#endif // ATOM_MEMORY_TRACKER_HPP \ No newline at end of file +#endif // ATOM_MEMORY_TRACKER_HPP diff --git a/atom/memory/utils.hpp b/atom/memory/utils.hpp index 97e8f750..d376357c 100644 --- a/atom/memory/utils.hpp +++ b/atom/memory/utils.hpp @@ -167,4 +167,4 @@ std::shared_ptr lockWeakOrCreate(std::weak_ptr& weak, Args&&... args) { } // namespace atom::memory -#endif // ATOM_MEMORY_UTILS_HPP \ No newline at end of file +#endif // ATOM_MEMORY_UTILS_HPP diff --git a/atom/memory/xmake.lua b/atom/memory/xmake.lua index bf601d04..1faa66f3 100644 --- a/atom/memory/xmake.lua +++ b/atom/memory/xmake.lua @@ -42,61 +42,61 @@ end target(lib_name) local sources = get_sources() local headers = get_headers() - + if #sources > 0 then -- Create library with source files set_kind("static") add_files(sources) add_headerfiles(headers) - + -- Add dependencies add_deps("atom-error") - + -- Set include directories add_includedirs(".", {public = true}) - + -- Enable position independent code add_cxflags("-fPIC", {tools = {"gcc", "clang"}}) add_cflags("-fPIC", {tools = {"gcc", "clang"}}) - + else -- Create header-only library set_kind("headeronly") add_headerfiles(headers) - + -- Add dependencies for header-only library add_deps("atom-error") - + -- Set include directories add_includedirs(".", {public = true}) end - + -- Set version set_version("1.0.0") - + -- Set output name set_basename(lib_name) - + -- Installation rules after_install(function (target) local installdir = target:installdir() or "$(prefix)" local kind = target:kind() - + if kind ~= "headeronly" then -- Install library file os.cp(target:targetfile(), path.join(installdir, "lib")) end - + -- Install headers local headerdir = path.join(installdir, "include", "atom", "memory") os.mkdir(headerdir) - + local headers = get_headers() for _, header in ipairs(headers) do os.cp(header, headerdir) end end) - + -- Add to global module list (equivalent to CMake's global property) after_build(function (target) -- Store module information for potential use by parent build system diff --git a/atom/meta/CMakeLists.txt b/atom/meta/CMakeLists.txt index 615a5c04..b3d1ff65 100644 --- a/atom/meta/CMakeLists.txt +++ b/atom/meta/CMakeLists.txt @@ -43,4 +43,4 @@ set_target_properties(${PROJECT_NAME} PROPERTIES # Install rules install(TARGETS ${PROJECT_NAME} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} -) \ No newline at end of file +) diff --git a/atom/meta/container_traits.hpp b/atom/meta/container_traits.hpp index fa20c087..5693ca04 100644 --- a/atom/meta/container_traits.hpp +++ b/atom/meta/container_traits.hpp @@ -845,4 +845,4 @@ auto make_container_pipe(Container&& container) { } // namespace atom::meta -#endif // ATOM_META_CONTAINER_TRAITS_HPP \ No newline at end of file +#endif // ATOM_META_CONTAINER_TRAITS_HPP diff --git a/atom/meta/facade.hpp b/atom/meta/facade.hpp index 5997ecb7..3d538b94 100644 --- a/atom/meta/facade.hpp +++ b/atom/meta/facade.hpp @@ -1088,4 +1088,4 @@ std::ostream& operator<<(std::ostream& os, const proxy& p) { return os; } -} // namespace atom::meta \ No newline at end of file +} // namespace atom::meta diff --git a/atom/meta/facade_any.hpp b/atom/meta/facade_any.hpp index 0c41da3f..a08e95c3 100644 --- a/atom/meta/facade_any.hpp +++ b/atom/meta/facade_any.hpp @@ -796,4 +796,4 @@ auto enhancedVarWithDesc(T&& value, std::string_view description) } // namespace atom::meta -#endif // ATOM_META_FACADE_ANY_HPP \ No newline at end of file +#endif // ATOM_META_FACADE_ANY_HPP diff --git a/atom/meta/facade_proxy.hpp b/atom/meta/facade_proxy.hpp index a5de1853..63f5a933 100644 --- a/atom/meta/facade_proxy.hpp +++ b/atom/meta/facade_proxy.hpp @@ -525,4 +525,4 @@ auto makeEnhancedProxy(Func&& func, std::string_view name) { } // namespace atom::meta -#endif // ATOM_META_FACADE_PROXY_HPP \ No newline at end of file +#endif // ATOM_META_FACADE_PROXY_HPP diff --git a/atom/meta/field_count.hpp b/atom/meta/field_count.hpp index 506f21a6..0e061b07 100644 --- a/atom/meta/field_count.hpp +++ b/atom/meta/field_count.hpp @@ -255,4 +255,4 @@ consteval auto fieldCountOf() -> std::size_t { } // namespace atom::meta -#endif // ATOM_META_FIELD_COUNT_HPP \ No newline at end of file +#endif // ATOM_META_FIELD_COUNT_HPP diff --git a/atom/meta/global_ptr.cpp b/atom/meta/global_ptr.cpp index 4e0adb75..64774f5a 100644 --- a/atom/meta/global_ptr.cpp +++ b/atom/meta/global_ptr.cpp @@ -183,4 +183,4 @@ void GlobalSharedPtrManager::updateMetadata(std::string_view key, // Ignore type errors in ref counting } } -} \ No newline at end of file +} diff --git a/atom/meta/god.hpp b/atom/meta/god.hpp index da4c537c..b8a416d9 100644 --- a/atom/meta/god.hpp +++ b/atom/meta/god.hpp @@ -791,4 +791,4 @@ T& singleton() { } // namespace atom::meta -#endif // ATOM_META_GOD_HPP \ No newline at end of file +#endif // ATOM_META_GOD_HPP diff --git a/atom/meta/invoke.hpp b/atom/meta/invoke.hpp index e11cdfe4..a3458b42 100644 --- a/atom/meta/invoke.hpp +++ b/atom/meta/invoke.hpp @@ -855,4 +855,4 @@ template } // namespace atom::meta -#endif // ATOM_META_INVOKE_HPP \ No newline at end of file +#endif // ATOM_META_INVOKE_HPP diff --git a/atom/meta/proxy.hpp b/atom/meta/proxy.hpp index 62ff9b6d..bb0d1b11 100644 --- a/atom/meta/proxy.hpp +++ b/atom/meta/proxy.hpp @@ -862,4 +862,4 @@ auto composeProxy(Func1&& f1, Func2&& f2) { } // namespace atom::meta -#endif \ No newline at end of file +#endif diff --git a/atom/meta/stepper.hpp b/atom/meta/stepper.hpp index 68ca4bde..81cfde73 100644 --- a/atom/meta/stepper.hpp +++ b/atom/meta/stepper.hpp @@ -971,4 +971,4 @@ class FunctionSequence { } // namespace atom::meta -#endif // ATOM_META_STEPPER_HPP \ No newline at end of file +#endif // ATOM_META_STEPPER_HPP diff --git a/atom/meta/type_info.hpp b/atom/meta/type_info.hpp index c05851f7..ab117547 100644 --- a/atom/meta/type_info.hpp +++ b/atom/meta/type_info.hpp @@ -697,4 +697,4 @@ struct hash { }; } // namespace std -#endif \ No newline at end of file +#endif diff --git a/atom/meta/xmake.lua b/atom/meta/xmake.lua index 098060be..1078b694 100644 --- a/atom/meta/xmake.lua +++ b/atom/meta/xmake.lua @@ -24,15 +24,15 @@ local header_files = { -- Object Library target("atom-meta-object") set_kind("object") - + -- Add files add_files(table.unpack(source_files)) add_headerfiles(table.unpack(header_files)) - + -- Add include directories add_includedirs(".", {public = true}) add_includedirs("..", {public = true}) - + -- Set C++ standard set_languages("c++20") target_end() @@ -41,17 +41,17 @@ target_end() target("atom-meta") -- Set library type based on parent project option set_kind(has_config("shared_libs") and "shared" or "static") - + -- Add dependencies add_deps("atom-meta-object") - + -- Set output directories set_targetdir("$(buildir)/lib") set_objectdir("$(buildir)/obj") - + -- Set version with build timestamp set_version("1.0.0", {build = "%Y%m%d%H%M"}) - + -- Install configuration on_install(function (target) os.cp(target:targetfile(), path.join(target:installdir(), "lib")) diff --git a/atom/search/lru.hpp b/atom/search/lru.hpp index 1019897a..2a6aa912 100644 --- a/atom/search/lru.hpp +++ b/atom/search/lru.hpp @@ -1282,4 +1282,4 @@ auto ThreadSafeLRUCache::acquireWriteLock( } // namespace atom::search -#endif // THREADSAFE_LRU_CACHE_H \ No newline at end of file +#endif // THREADSAFE_LRU_CACHE_H diff --git a/atom/search/mysql.cpp b/atom/search/mysql.cpp index da3e2b5b..bb639f7d 100644 --- a/atom/search/mysql.cpp +++ b/atom/search/mysql.cpp @@ -969,4 +969,4 @@ bool MysqlDB::setConnectionTimeout(unsigned int timeout) { } } // namespace database -} // namespace atom \ No newline at end of file +} // namespace atom diff --git a/atom/search/mysql.hpp b/atom/search/mysql.hpp index 994b1f45..72ed6c20 100644 --- a/atom/search/mysql.hpp +++ b/atom/search/mysql.hpp @@ -29,7 +29,7 @@ namespace database { /** * @brief Custom exception class for MySQL-related errors - * + * * This exception is thrown when MySQL operations fail or encounter errors. * It provides detailed error messages to help with debugging. */ @@ -37,7 +37,7 @@ class MySQLException : public std::runtime_error { public: /** * @brief Construct a new MySQL Exception object - * + * * @param message Error message describing the exception */ explicit MySQLException(const std::string& message) @@ -46,7 +46,7 @@ class MySQLException : public std::runtime_error { /** * @brief Structure to hold database connection parameters - * + * * This structure encapsulates all the necessary parameters needed * to establish a connection to a MySQL/MariaDB database. */ @@ -67,7 +67,7 @@ struct ConnectionParams { /** * @brief Enum for transaction isolation levels - * + * * Defines the different isolation levels available for database transactions, * controlling how transactions interact with each other. */ @@ -80,7 +80,7 @@ enum class TransactionIsolation { /** * @brief Class representing a database row - * + * * This class provides methods to access field values in different data types * from a single row of a MySQL result set. */ @@ -88,7 +88,7 @@ class Row { public: /** * @brief Construct a new Row object - * + * * @param row MySQL row data * @param lengths Array of field lengths * @param numFields Number of fields in the row @@ -97,7 +97,7 @@ class Row { /** * @brief Get a string value from the specified field - * + * * @param index Field index (0-based) * @return std::string Field value as string, empty if null or invalid index */ @@ -105,7 +105,7 @@ class Row { /** * @brief Get an integer value from the specified field - * + * * @param index Field index (0-based) * @return int Field value as integer, 0 if null or invalid index */ @@ -113,7 +113,7 @@ class Row { /** * @brief Get a 64-bit integer value from the specified field - * + * * @param index Field index (0-based) * @return int64_t Field value as 64-bit integer, 0 if null or invalid index */ @@ -121,7 +121,7 @@ class Row { /** * @brief Get a double value from the specified field - * + * * @param index Field index (0-based) * @return double Field value as double, 0.0 if null or invalid index */ @@ -129,7 +129,7 @@ class Row { /** * @brief Get a boolean value from the specified field - * + * * @param index Field index (0-based) * @return bool Field value as boolean, false if null or invalid index */ @@ -137,7 +137,7 @@ class Row { /** * @brief Check if the specified field is null - * + * * @param index Field index (0-based) * @return true if field is null, false otherwise */ @@ -145,7 +145,7 @@ class Row { /** * @brief Get the number of fields in this row - * + * * @return unsigned int Number of fields */ unsigned int getFieldCount() const { return numFields; } @@ -159,7 +159,7 @@ class Row { /** * @class ResultSet * @brief Represents the result of a MySQL query - * + * * This class wraps the MYSQL_RES structure and provides methods to navigate * through the result set, retrieve field values, field names, count rows and * columns. It implements iterator support for modern C++ iteration patterns. @@ -172,14 +172,14 @@ class ResultSet { public: /** * @brief Construct a new ResultSet object - * + * * @param result MySQL result set pointer */ explicit ResultSet(MYSQL_RES* result); /** * @brief Destroy the ResultSet object - * + * * Automatically frees the MySQL result set. */ ~ResultSet(); @@ -189,14 +189,14 @@ class ResultSet { /** * @brief Move constructor - * + * * @param other Source ResultSet to move from */ ResultSet(ResultSet&& other) noexcept; /** * @brief Move assignment operator - * + * * @param other Source ResultSet to move from * @return ResultSet& Reference to this object */ @@ -204,14 +204,14 @@ class ResultSet { /** * @brief Move to the next row in the result set - * + * * @return true if there is a next row, false if end of result set */ bool next(); /** * @brief Get the current row - * + * * @return Row Current row object * @throws std::runtime_error if no current row */ @@ -219,14 +219,14 @@ class ResultSet { /** * @brief Get the number of fields in the result set - * + * * @return unsigned int Number of fields */ unsigned int getFieldCount() const; /** * @brief Get the name of a field by index - * + * * @param index Field index (0-based) * @return std::string Field name, empty if invalid index */ @@ -234,14 +234,14 @@ class ResultSet { /** * @brief Get the total number of rows in the result set - * + * * @return unsigned long long Number of rows */ unsigned long long getRowCount() const; /** * @brief Reset the result set to the beginning - * + * * @return true if successful, false otherwise */ bool reset(); @@ -274,7 +274,7 @@ class ResultSet { /** * @brief Get iterator to the beginning of the result set - * + * * @return iterator Iterator to the first row */ iterator begin() { @@ -289,7 +289,7 @@ class ResultSet { /** * @brief Get iterator to the end of the result set - * + * * @return iterator Iterator representing end */ iterator end() { return iterator(this, true); } @@ -304,7 +304,7 @@ class ResultSet { /** * @brief Class for prepared statements - * + * * This class provides a safe way to execute SQL statements with parameters, * preventing SQL injection attacks and improving performance for repeated * queries. @@ -313,7 +313,7 @@ class PreparedStatement { public: /** * @brief Construct a new PreparedStatement object - * + * * @param connection MySQL connection handle * @param query SQL query with parameter placeholders (?) * @throws MySQLException if statement preparation fails @@ -322,7 +322,7 @@ class PreparedStatement { /** * @brief Destroy the PreparedStatement object - * + * * Automatically closes the MySQL statement. */ ~PreparedStatement(); @@ -332,14 +332,14 @@ class PreparedStatement { /** * @brief Move constructor - * + * * @param other Source PreparedStatement to move from */ PreparedStatement(PreparedStatement&& other) noexcept; /** * @brief Move assignment operator - * + * * @param other Source PreparedStatement to move from * @return PreparedStatement& Reference to this object */ @@ -347,7 +347,7 @@ class PreparedStatement { /** * @brief Bind a string parameter - * + * * @param index Parameter index (0-based) * @param value String value to bind * @return PreparedStatement& Reference to this object for method chaining @@ -356,7 +356,7 @@ class PreparedStatement { /** * @brief Bind an integer parameter - * + * * @param index Parameter index (0-based) * @param value Integer value to bind * @return PreparedStatement& Reference to this object for method chaining @@ -365,7 +365,7 @@ class PreparedStatement { /** * @brief Bind a 64-bit integer parameter - * + * * @param index Parameter index (0-based) * @param value 64-bit integer value to bind * @return PreparedStatement& Reference to this object for method chaining @@ -374,7 +374,7 @@ class PreparedStatement { /** * @brief Bind a double parameter - * + * * @param index Parameter index (0-based) * @param value Double value to bind * @return PreparedStatement& Reference to this object for method chaining @@ -383,7 +383,7 @@ class PreparedStatement { /** * @brief Bind a boolean parameter - * + * * @param index Parameter index (0-based) * @param value Boolean value to bind * @return PreparedStatement& Reference to this object for method chaining @@ -392,7 +392,7 @@ class PreparedStatement { /** * @brief Bind a null parameter - * + * * @param index Parameter index (0-based) * @return PreparedStatement& Reference to this object for method chaining */ @@ -400,14 +400,14 @@ class PreparedStatement { /** * @brief Execute the prepared statement - * + * * @return true if execution was successful, false otherwise */ bool execute(); /** * @brief Execute the prepared statement and return results - * + * * @return std::unique_ptr Result set containing query results * @throws MySQLException if execution fails */ @@ -415,7 +415,7 @@ class PreparedStatement { /** * @brief Execute an update/insert/delete statement - * + * * @return int Number of affected rows * @throws MySQLException if execution fails */ @@ -423,7 +423,7 @@ class PreparedStatement { /** * @brief Reset the statement for reuse - * + * * @throws MySQLException if reset fails */ void reset(); @@ -435,7 +435,7 @@ class PreparedStatement { /** * @brief Get the number of parameters in the statement - * + * * @return unsigned int Number of parameters */ unsigned int getParameterCount() const; @@ -451,7 +451,7 @@ class PreparedStatement { /** * @class MysqlDB * @brief Enhanced class for interacting with a MySQL/MariaDB database - * + * * This class provides a comprehensive interface for MySQL database operations * including connection management, query execution, transaction handling, * prepared statements, and error management. It is thread-safe and supports @@ -461,7 +461,7 @@ class MysqlDB { public: /** * @brief Constructor with connection parameters structure - * + * * @param params Connection parameters * @throws MySQLException if connection fails */ @@ -469,7 +469,7 @@ class MysqlDB { /** * @brief Constructor with individual connection parameters - * + * * @param host Database server hostname or IP * @param user Database username * @param password Database password @@ -494,14 +494,14 @@ class MysqlDB { /** * @brief Move constructor - * + * * @param other Source MysqlDB to move from */ MysqlDB(MysqlDB&& other) noexcept; /** * @brief Move assignment operator - * + * * @param other Source MysqlDB to move from * @return MysqlDB& Reference to this object */ @@ -509,14 +509,14 @@ class MysqlDB { /** * @brief Connect to the database with stored parameters - * + * * @return true if connection successful, false otherwise */ bool connect(); /** * @brief Reconnect to the database if connection was lost - * + * * @return true if reconnection successful, false otherwise */ bool reconnect(); @@ -528,14 +528,14 @@ class MysqlDB { /** * @brief Check if the connection is alive - * + * * @return true if connected, false otherwise */ bool isConnected(); /** * @brief Execute a SQL query without returning results - * + * * @param query SQL query string * @return true if execution successful, false otherwise */ @@ -543,7 +543,7 @@ class MysqlDB { /** * @brief Execute a query and return results - * + * * @param query SQL SELECT query string * @return std::unique_ptr Result set containing query results * @throws MySQLException if execution fails @@ -552,7 +552,7 @@ class MysqlDB { /** * @brief Execute a data modification query and return affected rows - * + * * @param query SQL INSERT/UPDATE/DELETE query * @return int Number of affected rows, -1 if error * @throws MySQLException if execution fails @@ -561,7 +561,7 @@ class MysqlDB { /** * @brief Get a single integer value from a query - * + * * @param query SQL query that returns a single integer * @return std::optional Integer value if successful, nullopt otherwise */ @@ -569,7 +569,7 @@ class MysqlDB { /** * @brief Get a single double value from a query - * + * * @param query SQL query that returns a single double * @return std::optional Double value if successful, nullopt otherwise */ @@ -577,7 +577,7 @@ class MysqlDB { /** * @brief Get a single string value from a query - * + * * @param query SQL query that returns a single string * @return std::optional String value if successful, nullopt otherwise */ @@ -585,7 +585,7 @@ class MysqlDB { /** * @brief Search for data matching criteria - * + * * @param query Base SQL query * @param column Column name to search in * @param searchTerm Term to search for @@ -596,7 +596,7 @@ class MysqlDB { /** * @brief Create a prepared statement for safe query execution - * + * * @param query SQL query with parameter placeholders (?) * @return std::unique_ptr Prepared statement object * @throws MySQLException if preparation fails @@ -605,28 +605,28 @@ class MysqlDB { /** * @brief Begin a database transaction - * + * * @return true if transaction started successfully, false otherwise */ bool beginTransaction(); /** * @brief Commit the current transaction - * + * * @return true if transaction committed successfully, false otherwise */ bool commitTransaction(); /** * @brief Rollback the current transaction - * + * * @return true if transaction rolled back successfully, false otherwise */ bool rollbackTransaction(); /** * @brief Set a savepoint within a transaction - * + * * @param savepointName Name of the savepoint * @return true if savepoint created successfully, false otherwise */ @@ -634,7 +634,7 @@ class MysqlDB { /** * @brief Rollback to a specific savepoint - * + * * @param savepointName Name of the savepoint * @return true if rollback successful, false otherwise */ @@ -642,7 +642,7 @@ class MysqlDB { /** * @brief Set transaction isolation level - * + * * @param level Isolation level to set * @return true if isolation level set successfully, false otherwise */ @@ -650,7 +650,7 @@ class MysqlDB { /** * @brief Execute multiple queries in sequence - * + * * @param queries Vector of SQL queries to execute * @return true if all queries executed successfully, false otherwise */ @@ -658,7 +658,7 @@ class MysqlDB { /** * @brief Execute multiple queries within a transaction - * + * * @param queries Vector of SQL queries to execute * @return true if all queries executed successfully, false if any failed (transaction rolled back) */ @@ -666,7 +666,7 @@ class MysqlDB { /** * @brief Execute operations within a transaction with automatic rollback - * + * * @param operations Function containing database operations to execute * @throws Re-throws any exceptions from operations after rollback */ @@ -674,7 +674,7 @@ class MysqlDB { /** * @brief Call a stored procedure - * + * * @param procedureName Name of the stored procedure * @param params Vector of parameters for the procedure * @return std::unique_ptr Result set if procedure returns data @@ -685,7 +685,7 @@ class MysqlDB { /** * @brief Get list of databases on the server - * + * * @return std::vector Vector of database names * @throws MySQLException if query fails */ @@ -693,7 +693,7 @@ class MysqlDB { /** * @brief Get list of tables in the current database - * + * * @return std::vector Vector of table names * @throws MySQLException if query fails */ @@ -701,7 +701,7 @@ class MysqlDB { /** * @brief Get list of columns for a specific table - * + * * @param tableName Name of the table * @return std::vector Vector of column names * @throws MySQLException if query fails @@ -710,7 +710,7 @@ class MysqlDB { /** * @brief Check if a table exists in the database - * + * * @param tableName Name of the table to check * @return true if table exists, false otherwise */ @@ -718,21 +718,21 @@ class MysqlDB { /** * @brief Get the last error message - * + * * @return std::string Error message */ std::string getLastError() const; /** * @brief Get the last error code - * + * * @return unsigned int Error code */ unsigned int getLastErrorCode() const; /** * @brief Set a custom error callback function - * + * * @param callback Function to call when errors occur */ void setErrorCallback( @@ -740,7 +740,7 @@ class MysqlDB { /** * @brief Escape a string for safe use in SQL queries - * + * * @param str String to escape * @return std::string Escaped string * @throws MySQLException if not connected @@ -749,21 +749,21 @@ class MysqlDB { /** * @brief Get the ID of the last inserted row - * + * * @return unsigned long long Last insert ID */ unsigned long long getLastInsertId() const; /** * @brief Get the number of rows affected by the last statement - * + * * @return unsigned long long Number of affected rows */ unsigned long long getAffectedRows() const; /** * @brief Execute a query with pagination - * + * * @param query Base SQL SELECT query * @param limit Maximum number of rows to return * @param offset Number of rows to skip @@ -775,28 +775,28 @@ class MysqlDB { /** * @brief Get database server version - * + * * @return std::string Server version string */ std::string getServerVersion() const; /** * @brief Get client library version - * + * * @return std::string Client library version string */ std::string getClientVersion() const; /** * @brief Ping the server to check connection - * + * * @return true if connection is alive, false otherwise */ bool ping(); /** * @brief Set connection timeout - * + * * @param timeout Timeout in seconds * @return true if timeout set successfully, false otherwise */ @@ -811,7 +811,7 @@ class MysqlDB { /** * @brief Handle database errors - * + * * @param operation Description of the operation that failed * @param throwOnError Whether to throw exception on error * @return true if error occurred, false otherwise @@ -827,4 +827,4 @@ class MysqlDB { } // namespace database } // namespace atom -#endif // ATOM_SEARCH_MYSQL_HPP \ No newline at end of file +#endif // ATOM_SEARCH_MYSQL_HPP diff --git a/atom/search/search.cpp b/atom/search/search.cpp index a8b09afe..18a2d6b6 100644 --- a/atom/search/search.cpp +++ b/atom/search/search.cpp @@ -1177,4 +1177,4 @@ std::vector> SearchEngine::getRankedResults( return results; } -} // namespace atom::search \ No newline at end of file +} // namespace atom::search diff --git a/atom/search/sqlite.cpp b/atom/search/sqlite.cpp index f5ccd179..882bc1da 100644 --- a/atom/search/sqlite.cpp +++ b/atom/search/sqlite.cpp @@ -859,4 +859,4 @@ bool SqliteDB::analyze() { template std::optional SqliteDB::getSingleValue( std::string_view query, int (*columnFunc)(sqlite3_stmt*, int)); template std::optional SqliteDB::getSingleValue( - std::string_view query, double (*columnFunc)(sqlite3_stmt*, int)); \ No newline at end of file + std::string_view query, double (*columnFunc)(sqlite3_stmt*, int)); diff --git a/atom/search/sqlite.hpp b/atom/search/sqlite.hpp index 108d4a84..6ede9857 100644 --- a/atom/search/sqlite.hpp +++ b/atom/search/sqlite.hpp @@ -24,7 +24,7 @@ using atom::containers::Vector; /** * @brief Custom exception class for SQLite operations - * + * * This exception is thrown when SQLite operations fail or encounter errors. * It provides detailed error messages to help with debugging. */ @@ -35,14 +35,14 @@ class SQLiteException : public std::exception { public: /** * @brief Construct a new SQLite Exception object - * + * * @param msg Error message describing the exception */ explicit SQLiteException(std::string_view msg) : message(msg) {} - + /** * @brief Get the exception message - * + * * @return const char* Null-terminated error message string */ [[nodiscard]] const char* what() const noexcept override { @@ -53,7 +53,7 @@ class SQLiteException : public std::exception { /** * @class SqliteDB * @brief A thread-safe SQLite database wrapper with advanced features - * + * * This class provides a high-level interface for SQLite database operations * including prepared statement caching, transaction management, and thread safety. * It uses the Pimpl design pattern for implementation hiding and better @@ -73,7 +73,7 @@ class SqliteDB { /** * @brief Construct a new SqliteDB object - * + * * @param dbPath Path to the SQLite database file * @throws SQLiteException if the database cannot be opened */ @@ -81,7 +81,7 @@ class SqliteDB { /** * @brief Destroy the SqliteDB object - * + * * Automatically closes the database connection and cleans up resources. */ ~SqliteDB(); @@ -91,14 +91,14 @@ class SqliteDB { /** * @brief Move constructor - * + * * @param other Source object to move from */ SqliteDB(SqliteDB&& other) noexcept; /** * @brief Move assignment operator - * + * * @param other Source object to move from * @return SqliteDB& Reference to this object */ @@ -106,7 +106,7 @@ class SqliteDB { /** * @brief Execute a simple SQL query without parameters - * + * * @param query SQL query string to execute * @return true if execution was successful * @throws SQLiteException on execution error @@ -115,10 +115,10 @@ class SqliteDB { /** * @brief Execute a parameterized SQL query with bound values - * + * * This method uses prepared statements for security and performance. * Parameters are automatically bound based on their types. - * + * * @tparam Args Parameter types to bind * @param query SQL query with placeholders (?) * @param params Parameters to bind to the query @@ -131,7 +131,7 @@ class SqliteDB { /** * @brief Execute a SELECT query and return all results - * + * * @param query SQL SELECT query string * @return ResultSet containing all rows from the query * @throws SQLiteException on query error @@ -140,7 +140,7 @@ class SqliteDB { /** * @brief Execute a parameterized SELECT query and return results - * + * * @tparam Args Parameter types to bind * @param query SQL SELECT query with placeholders * @param params Parameters to bind to the query @@ -153,7 +153,7 @@ class SqliteDB { /** * @brief Helper function to retrieve a single value of any type - * + * * @tparam T Type of value to retrieve * @param query SQL query that returns a single value * @param columnFunc Function to extract value from SQLite column @@ -165,7 +165,7 @@ class SqliteDB { /** * @brief Retrieve a single integer value from a query - * + * * @param query SQL query that returns a single integer * @return Optional integer value */ @@ -173,7 +173,7 @@ class SqliteDB { /** * @brief Retrieve a single floating-point value from a query - * + * * @param query SQL query that returns a single double * @return Optional double value */ @@ -181,7 +181,7 @@ class SqliteDB { /** * @brief Retrieve a single text value from a query - * + * * @param query SQL query that returns a single text value * @return Optional String value */ @@ -189,7 +189,7 @@ class SqliteDB { /** * @brief Search for data matching a specific term - * + * * @param query SQL query with a single parameter placeholder * @param searchTerm Term to search for * @return true if matching data was found @@ -199,7 +199,7 @@ class SqliteDB { /** * @brief Execute an UPDATE statement and return affected row count - * + * * @param query SQL UPDATE statement * @return Number of rows affected by the update * @throws SQLiteException on update error @@ -208,7 +208,7 @@ class SqliteDB { /** * @brief Execute a DELETE statement and return affected row count - * + * * @param query SQL DELETE statement * @return Number of rows affected by the delete * @throws SQLiteException on delete error @@ -217,23 +217,23 @@ class SqliteDB { /** * @brief Begin a database transaction - * + * * Uses IMMEDIATE transaction mode for better concurrency control. - * + * * @throws SQLiteException if transaction cannot be started */ void beginTransaction(); /** * @brief Commit the current transaction - * + * * @throws SQLiteException if transaction cannot be committed */ void commitTransaction(); /** * @brief Rollback the current transaction - * + * * This method does not throw exceptions to ensure it can be safely * called from destructors and error handlers. */ @@ -241,10 +241,10 @@ class SqliteDB { /** * @brief Execute operations within a transaction with automatic rollback - * + * * Automatically begins a transaction, executes the provided operations, * and commits. If any exception occurs, the transaction is rolled back. - * + * * @param operations Function containing database operations to execute * @throws Re-throws any exceptions from operations after rollback */ @@ -252,10 +252,10 @@ class SqliteDB { /** * @brief Validate data using a validation query - * + * * Executes the main query, then runs a validation query to check * if the operation was successful. - * + * * @param query Main SQL query to execute * @param validationQuery Query that should return non-zero for success * @return true if validation passes @@ -265,7 +265,7 @@ class SqliteDB { /** * @brief Execute a SELECT query with pagination - * + * * @param query Base SQL SELECT query (without LIMIT/OFFSET) * @param limit Maximum number of rows to return * @param offset Number of rows to skip @@ -277,7 +277,7 @@ class SqliteDB { /** * @brief Set a custom error message callback - * + * * @param errorCallback Function to call when errors occur */ void setErrorMessageCallback( @@ -285,14 +285,14 @@ class SqliteDB { /** * @brief Check if the database connection is active - * + * * @return true if connected to a database */ [[nodiscard]] bool isConnected() const noexcept; /** * @brief Get the rowid of the last inserted row - * + * * @return Row ID of the last insert operation * @throws SQLiteException if not connected */ @@ -300,7 +300,7 @@ class SqliteDB { /** * @brief Get the number of rows modified by the last statement - * + * * @return Number of rows affected by the last INSERT/UPDATE/DELETE * @throws SQLiteException if not connected */ @@ -308,7 +308,7 @@ class SqliteDB { /** * @brief Get the total number of rows modified since database opened - * + * * @return Total number of rows modified * @throws SQLiteException if not connected */ @@ -316,7 +316,7 @@ class SqliteDB { /** * @brief Check if a table exists in the database - * + * * @param tableName Name of the table to check * @return true if the table exists */ @@ -324,7 +324,7 @@ class SqliteDB { /** * @brief Get the schema information for a table - * + * * @param tableName Name of the table * @return ResultSet containing column information */ @@ -332,14 +332,14 @@ class SqliteDB { /** * @brief Execute VACUUM command to optimize database - * + * * @return true if VACUUM was successful */ [[nodiscard]] bool vacuum(); /** * @brief Execute ANALYZE command to update query planner statistics - * + * * @return true if ANALYZE was successful */ [[nodiscard]] bool analyze(); @@ -351,7 +351,7 @@ class SqliteDB { /** * @brief Validate query string for basic security checks - * + * * @param query Query string to validate * @throws SQLiteException if query is invalid */ @@ -359,14 +359,14 @@ class SqliteDB { /** * @brief Check database connection before operations - * + * * @throws SQLiteException if database is not connected */ void checkConnection() const; /** * @brief Helper for update/delete operations - * + * * @param query SQL statement to execute * @return Number of rows affected * @throws SQLiteException on error @@ -378,4 +378,4 @@ class SqliteDB { #endif }; -#endif // ATOM_SEARCH_SQLITE_HPP \ No newline at end of file +#endif // ATOM_SEARCH_SQLITE_HPP diff --git a/atom/search/ttl.hpp b/atom/search/ttl.hpp index cbfa6bfa..22a1c931 100644 --- a/atom/search/ttl.hpp +++ b/atom/search/ttl.hpp @@ -41,25 +41,25 @@ namespace atom::search { #if defined(ATOM_USE_BOOST_THREAD) template using SharedMutex = boost::shared_mutex; - + template using SharedLock = boost::shared_lock; - + template using UniqueLock = boost::unique_lock; - + using CondVarAny = boost::condition_variable_any; using Thread = boost::thread; #else template using SharedMutex = std::shared_mutex; - + template using SharedLock = std::shared_lock; - + template using UniqueLock = std::unique_lock; - + using CondVarAny = std::condition_variable_any; using Thread = std::thread; #endif @@ -119,7 +119,7 @@ struct CacheConfig { * @tparam Hash The hash function type for keys (defaults to std::hash). * @tparam KeyEqual The key equality comparison type (defaults to std::equal_to). */ -template , typename KeyEqual = std::equal_to> class TTLCache { @@ -142,7 +142,7 @@ class TTLCache { * @param eviction_callback Optional callback for eviction events. * @throws TTLCacheException if ttl <= 0 or max_capacity == 0 */ - explicit TTLCache(Duration ttl, + explicit TTLCache(Duration ttl, size_t max_capacity, std::optional cleanup_interval = std::nullopt, CacheConfig config = CacheConfig{}, @@ -175,7 +175,7 @@ class TTLCache { * @throws std::bad_alloc if memory allocation fails * @throws TTLCacheException for other internal errors */ - void put(const Key& key, const Value& value, + void put(const Key& key, const Value& value, std::optional custom_ttl = std::nullopt); /** @@ -239,7 +239,7 @@ class TTLCache { * @param update_access_time Whether to update access times (default: true). * @return Vector of optional values corresponding to the keys. */ - [[nodiscard]] ValueContainer batch_get(const KeyContainer& keys, + [[nodiscard]] ValueContainer batch_get(const KeyContainer& keys, bool update_access_time = true); /** @@ -410,7 +410,7 @@ class TTLCache { ValuePtr value; TimePoint expiry_time; TimePoint access_time; - + CacheItem(const Key& k, const Value& v, const TimePoint& expiry, const TimePoint& access); CacheItem(const Key& k, Value&& v, const TimePoint& expiry, const TimePoint& access); template @@ -430,7 +430,7 @@ class TTLCache { CacheMap cache_map_; mutable SharedMutex mutex_; - + Atomic hit_count_{0}; Atomic miss_count_{0}; Atomic eviction_count_{0}; @@ -459,7 +459,7 @@ TTLCache::TTLCache( max_capacity_(max_capacity), config_(std::move(config)), eviction_callback_(std::move(eviction_callback)) { - + if (ttl <= Duration::zero()) { throw TTLCacheException("TTL must be greater than zero"); } @@ -499,7 +499,7 @@ TTLCache::TTLCache(TTLCache&& other) noexcept miss_count_(other.miss_count_.load()), eviction_count_(other.eviction_count_.load()), expiration_count_(other.expiration_count_.load()) { - + UniqueLock lock(other.mutex_); cache_list_ = std::move(other.cache_list_); cache_map_ = std::move(other.cache_map_); @@ -517,7 +517,7 @@ TTLCache::TTLCache(TTLCache&& other) noexcept } template -TTLCache& +TTLCache& TTLCache::operator=(TTLCache&& other) noexcept { if (this != &other) { stop_flag_ = true; @@ -559,7 +559,7 @@ TTLCache::operator=(TTLCache&& other) noexcept { template void TTLCache::put( const Key& key, const Value& value, std::optional custom_ttl) { - + try { UniqueLock lock(mutex_); auto now = Clock::now(); @@ -587,7 +587,7 @@ void TTLCache::put( template void TTLCache::put( const Key& key, Value&& value, std::optional custom_ttl) { - + try { UniqueLock lock(mutex_); auto now = Clock::now(); @@ -616,7 +616,7 @@ template template void TTLCache::emplace( const Key& key, std::optional custom_ttl, Args&&... args) { - + try { UniqueLock lock(mutex_); auto now = Clock::now(); @@ -645,7 +645,7 @@ template void TTLCache::batch_put( const std::vector>& items, std::optional custom_ttl) { - + if (items.empty()) return; try { @@ -657,7 +657,7 @@ void TTLCache::batch_put( for (const auto& [key, value] : items) { auto expiry = now + ttl_to_use; - + auto it = cache_map_.find(key); if (it != cache_map_.end()) { notify_eviction(it->second->key, *(it->second->value), false); @@ -680,7 +680,7 @@ void TTLCache::batch_put( template std::optional TTLCache::get( const Key& key, bool update_access_time) { - + try { if (config_.thread_safe) { SharedLock lock(mutex_); @@ -698,9 +698,9 @@ std::optional TTLCache::get( } template -typename TTLCache::ValuePtr +typename TTLCache::ValuePtr TTLCache::get_shared(const Key& key, bool update_access_time) { - + try { if (config_.thread_safe) { SharedLock lock(mutex_); @@ -721,7 +721,7 @@ template typename TTLCache::ValueContainer TTLCache::batch_get( const KeyContainer& keys, bool update_access_time) { - + if (keys.empty()) return {}; ValueContainer results; @@ -735,12 +735,12 @@ TTLCache::batch_get( auto it = cache_map_.find(key); if (it != cache_map_.end() && !is_expired(it->second->expiry_time)) { if (config_.enable_statistics) hit_count_++; - + if (update_access_time) { it->second->access_time = now; move_to_front(it->second); } - + results.emplace_back(*(it->second->value)); } else { if (config_.enable_statistics) miss_count_++; @@ -761,7 +761,7 @@ template template Value TTLCache::get_or_compute( const Key& key, Factory&& factory, std::optional custom_ttl) { - + auto cached_value = get_shared(key); if (cached_value) { return *cached_value; @@ -880,7 +880,7 @@ CacheStatistics TTLCache::get_statistics() const noe stats.expirations = expiration_count_.load(); stats.current_size = cache_map_.size(); stats.max_capacity = max_capacity_; - + size_t total = stats.hits + stats.misses; stats.hit_rate = total > 0 ? static_cast(stats.hits) / total : 0.0; } catch (...) { @@ -901,7 +901,7 @@ void TTLCache::reset_statistics() noexcept { template double TTLCache::hit_rate() const noexcept { if (!config_.enable_statistics) return 0.0; - + size_t hits = hit_count_.load(); size_t misses = miss_count_.load(); size_t total = hits + misses; @@ -931,7 +931,7 @@ TTLCache::get_keys() const { SharedLock lock(mutex_); auto now = Clock::now(); keys.reserve(cache_map_.size()); - + for (const auto& [key, iter] : cache_map_) { if (!is_expired(iter->expiry_time)) { keys.push_back(key); @@ -946,16 +946,16 @@ template void TTLCache::clear() noexcept { try { UniqueLock lock(mutex_); - + if (eviction_callback_) { for (const auto& item : cache_list_) { notify_eviction(item.key, *(item.value), false); } } - + cache_list_.clear(); cache_map_.clear(); - + if (config_.enable_statistics) { hit_count_ = 0; miss_count_ = 0; @@ -1038,7 +1038,7 @@ template template TTLCache::CacheItem::CacheItem( const Key& k, const TimePoint& expiry, const TimePoint& access, Args&&... args) - : key(k), value(std::make_shared(std::forward(args)...)), + : key(k), value(std::make_shared(std::forward(args)...)), expiry_time(expiry), access_time(access) {} template @@ -1062,7 +1062,7 @@ void TTLCache::cleaner_task() noexcept { template void TTLCache::evict_items( UniqueLock& lock, size_t count) noexcept { - + try { auto now = Clock::now(); size_t expired_removed = 0; @@ -1080,7 +1080,7 @@ void TTLCache::evict_items( cache_map_.erase(key); --count; ++expired_removed; - + if (config_.enable_statistics) { expiration_count_++; } @@ -1095,7 +1095,7 @@ void TTLCache::evict_items( cache_map_.erase(last.key); cache_list_.pop_back(); --count; - + if (config_.enable_statistics) { eviction_count_++; } @@ -1130,11 +1130,11 @@ inline bool TTLCache::is_expired(const TimePoint& ex template void TTLCache::cleanup_expired_items( UniqueLock& lock) noexcept { - + try { auto now = Clock::now(); size_t batch_count = 0; - + auto it = cache_list_.begin(); while (it != cache_list_.end() && batch_count < config_.cleanup_batch_size) { if (is_expired(it->expiry_time)) { @@ -1142,10 +1142,10 @@ void TTLCache::cleanup_expired_items( auto value = it->value; it = cache_list_.erase(it); cache_map_.erase(key); - + notify_eviction(key, *value, true); ++batch_count; - + if (config_.enable_statistics) { expiration_count_++; } @@ -1159,4 +1159,4 @@ void TTLCache::cleanup_expired_items( } // namespace atom::search -#endif // ATOM_SEARCH_TTL_CACHE_HPP \ No newline at end of file +#endif // ATOM_SEARCH_TTL_CACHE_HPP diff --git a/atom/search/xmake.lua b/atom/search/xmake.lua index c3a39f3c..e9a7d55b 100644 --- a/atom/search/xmake.lua +++ b/atom/search/xmake.lua @@ -15,25 +15,25 @@ set_license("GPL3") -- Object Library target("atom-search-object") set_kind("object") - + -- Add source files add_files("*.cpp") - + -- Add header files add_headerfiles("*.hpp") - + -- Add dependencies add_packages("loguru") - + -- Add include directories add_includedirs(".", {public = true}) add_includedirs("..", {public = true}) - + -- Platform-specific settings if is_plat("linux") then add_syslinks("pthread") end - + -- Set C++ standard set_languages("c++20") target_end() @@ -42,20 +42,20 @@ target_end() target("atom-search") -- Set library type based on parent project option set_kind(has_config("shared_libs") and "shared" or "static") - + -- Add dependencies add_deps("atom-search-object") add_packages("loguru") - + -- Platform-specific settings if is_plat("linux") then add_syslinks("pthread") end - + -- Set output directories set_targetdir("$(buildir)/lib") set_objectdir("$(buildir)/obj") - + -- Install configuration on_install(function (target) os.cp(target:targetfile(), path.join(target:installdir(), "lib")) diff --git a/atom/secret/CMakeLists.txt b/atom/secret/CMakeLists.txt index e11ab9c7..d09b95a7 100644 --- a/atom/secret/CMakeLists.txt +++ b/atom/secret/CMakeLists.txt @@ -59,4 +59,4 @@ install(TARGETS ${PROJECT_NAME} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} PUBLIC_HEADER DESTINATION include/${PROJECT_NAME} -) \ No newline at end of file +) diff --git a/atom/secret/common.hpp b/atom/secret/common.hpp index 88a830f6..bca06b35 100644 --- a/atom/secret/common.hpp +++ b/atom/secret/common.hpp @@ -71,4 +71,4 @@ struct PreviousPassword { } // namespace atom::secret -#endif // ATOM_SECRET_COMMON_HPP \ No newline at end of file +#endif // ATOM_SECRET_COMMON_HPP diff --git a/atom/secret/encryption.cpp b/atom/secret/encryption.cpp index 4693fc9b..859852ee 100644 --- a/atom/secret/encryption.cpp +++ b/atom/secret/encryption.cpp @@ -37,4 +37,4 @@ SslCipherContext& SslCipherContext::operator=( return *this; } -} // namespace atom::secret \ No newline at end of file +} // namespace atom::secret diff --git a/atom/secret/encryption.hpp b/atom/secret/encryption.hpp index 166b16dd..93d1dfde 100644 --- a/atom/secret/encryption.hpp +++ b/atom/secret/encryption.hpp @@ -51,4 +51,4 @@ class SslCipherContext { } // namespace atom::secret -#endif // ATOM_SECRET_ENCRYPTION_HPP \ No newline at end of file +#endif // ATOM_SECRET_ENCRYPTION_HPP diff --git a/atom/secret/password_entry.hpp b/atom/secret/password_entry.hpp index 0ab685ad..0134b859 100644 --- a/atom/secret/password_entry.hpp +++ b/atom/secret/password_entry.hpp @@ -47,4 +47,4 @@ namespace atom::secret { } // namespace atom::secret -#endif // ATOM_SECRET_PASSWORD_ENTRY_HPP \ No newline at end of file +#endif // ATOM_SECRET_PASSWORD_ENTRY_HPP diff --git a/atom/secret/result.hpp b/atom/secret/result.hpp index cc79700b..92e6a4b7 100644 --- a/atom/secret/result.hpp +++ b/atom/secret/result.hpp @@ -91,4 +91,4 @@ class Result { } // namespace atom::secret -#endif // ATOM_SECRET_RESULT_HPP \ No newline at end of file +#endif // ATOM_SECRET_RESULT_HPP diff --git a/atom/secret/storage.cpp b/atom/secret/storage.cpp index 8934223a..1837e260 100644 --- a/atom/secret/storage.cpp +++ b/atom/secret/storage.cpp @@ -893,4 +893,4 @@ std::unique_ptr SecureStorage::create(std::string_view appName) { #endif } -} // namespace atom::secret \ No newline at end of file +} // namespace atom::secret diff --git a/atom/secret/storage.hpp b/atom/secret/storage.hpp index 3a628e55..9cbae8e4 100644 --- a/atom/secret/storage.hpp +++ b/atom/secret/storage.hpp @@ -54,4 +54,4 @@ class SecureStorage { } // namespace atom::secret -#endif // ATOM_SECRET_STORAGE_HPP \ No newline at end of file +#endif // ATOM_SECRET_STORAGE_HPP diff --git a/atom/secret/xmake.lua b/atom/secret/xmake.lua index 8402c633..c78603a0 100644 --- a/atom/secret/xmake.lua +++ b/atom/secret/xmake.lua @@ -29,19 +29,19 @@ local header_files = { -- Object Library target("atom-secret-object") set_kind("object") - + -- Add files add_files(table.unpack(source_files)) add_headerfiles(table.unpack(header_files)) - + -- Add dependencies add_packages("loguru") add_deps("atom-utils") - + -- Add include directories add_includedirs(".", {public = true}) add_includedirs("..", {public = true}) - + -- Platform-specific settings if is_plat("windows") then add_syslinks("crypt32", "advapi32") @@ -50,7 +50,7 @@ target("atom-secret-object") elseif is_plat("macosx") then add_frameworks("Security") end - + -- Set C++ standard set_languages("c++20") target_end() @@ -59,11 +59,11 @@ target_end() target("atom-secret") -- Set library type based on parent project option set_kind(has_config("shared_libs") and "shared" or "static") - + -- Add dependencies add_deps("atom-secret-object", "atom-utils") add_packages("loguru") - + -- Platform-specific settings if is_plat("windows") then add_syslinks("crypt32", "advapi32") @@ -72,11 +72,11 @@ target("atom-secret") elseif is_plat("macosx") then add_frameworks("Security") end - + -- Set output directories set_targetdir("$(buildir)/lib") set_objectdir("$(buildir)/obj") - + -- Install configuration on_install(function (target) os.cp(target:targetfile(), path.join(target:installdir(), "lib")) diff --git a/atom/serial/CMakeLists.txt b/atom/serial/CMakeLists.txt index d63f6913..4060bffb 100644 --- a/atom/serial/CMakeLists.txt +++ b/atom/serial/CMakeLists.txt @@ -27,7 +27,7 @@ target_include_directories(${LIB_NAME} PUBLIC # Set platform-specific dependencies if(WIN32) - target_link_libraries(${LIB_NAME} + target_link_libraries(${LIB_NAME} PUBLIC atom-error atom-log @@ -37,7 +37,7 @@ if(WIN32) elseif(APPLE) find_library(IOKIT_FRAMEWORK IOKit REQUIRED) find_library(FOUNDATION_FRAMEWORK Foundation REQUIRED) - target_link_libraries(${LIB_NAME} + target_link_libraries(${LIB_NAME} PUBLIC atom-error atom-log @@ -48,13 +48,13 @@ else() # Linux/Unix find_package(PkgConfig REQUIRED) pkg_check_modules(UDEV REQUIRED libudev) pkg_check_modules(LIBUSB REQUIRED libusb-1.0) - + target_include_directories(${LIB_NAME} PUBLIC ${UDEV_INCLUDE_DIRS} ${LIBUSB_INCLUDE_DIRS} ) - - target_link_libraries(${LIB_NAME} + + target_link_libraries(${LIB_NAME} PUBLIC atom-error atom-log diff --git a/atom/serial/bluetooth_serial.cpp b/atom/serial/bluetooth_serial.cpp index 2349c1df..10c1a07d 100644 --- a/atom/serial/bluetooth_serial.cpp +++ b/atom/serial/bluetooth_serial.cpp @@ -117,4 +117,4 @@ BluetoothSerial::Statistics BluetoothSerial::getStatistics() const { return impl_->getStatistics(); } -} // namespace serial \ No newline at end of file +} // namespace serial diff --git a/atom/serial/bluetooth_serial.hpp b/atom/serial/bluetooth_serial.hpp index f7a01b4d..2c4b29c7 100644 --- a/atom/serial/bluetooth_serial.hpp +++ b/atom/serial/bluetooth_serial.hpp @@ -324,4 +324,4 @@ class BluetoothSerial { impl_; // PIMPL pattern for platform implementation }; -} // namespace serial \ No newline at end of file +} // namespace serial diff --git a/atom/serial/bluetooth_serial_mac.hpp b/atom/serial/bluetooth_serial_mac.hpp index 0bf46288..ee1b4731 100644 --- a/atom/serial/bluetooth_serial_mac.hpp +++ b/atom/serial/bluetooth_serial_mac.hpp @@ -257,4 +257,4 @@ class BluetoothSerialImpl { } // namespace serial -#endif // __APPLE__ \ No newline at end of file +#endif // __APPLE__ diff --git a/atom/serial/bluetooth_serial_mac.mm b/atom/serial/bluetooth_serial_mac.mm index 445ce9be..49e89c81 100644 --- a/atom/serial/bluetooth_serial_mac.mm +++ b/atom/serial/bluetooth_serial_mac.mm @@ -487,4 +487,4 @@ void stopAsyncWorker() { } // namespace serial -#endif // __APPLE__ \ No newline at end of file +#endif // __APPLE__ diff --git a/atom/serial/bluetooth_serial_unix.hpp b/atom/serial/bluetooth_serial_unix.hpp index 54d2a29f..97533b4b 100644 --- a/atom/serial/bluetooth_serial_unix.hpp +++ b/atom/serial/bluetooth_serial_unix.hpp @@ -882,4 +882,4 @@ class BluetoothSerialImpl { } // namespace serial -#endif // defined(__linux__) \ No newline at end of file +#endif // defined(__linux__) diff --git a/atom/serial/bluetooth_serial_win.hpp b/atom/serial/bluetooth_serial_win.hpp index 2a10aeb8..3d25ded7 100644 --- a/atom/serial/bluetooth_serial_win.hpp +++ b/atom/serial/bluetooth_serial_win.hpp @@ -638,4 +638,4 @@ class BluetoothSerialImpl { } // namespace serial -#endif // _WIN32 \ No newline at end of file +#endif // _WIN32 diff --git a/atom/serial/scanner.cpp b/atom/serial/scanner.cpp index 26e816f9..67193d6f 100644 --- a/atom/serial/scanner.cpp +++ b/atom/serial/scanner.cpp @@ -1269,7 +1269,7 @@ SerialPortScanner::get_port_details_linux(std::string_view port_name) { } catch (const std::exception& e) { if (config_.enable_debug_logging) { - spdlog::warn("Failed to get Linux port details for {}: {}", + spdlog::warn("Failed to get Linux port details for {}: {}", port_name, e.what()); } } diff --git a/atom/serial/serial_port.cpp b/atom/serial/serial_port.cpp index 925cd582..81004772 100644 --- a/atom/serial/serial_port.cpp +++ b/atom/serial/serial_port.cpp @@ -31,12 +31,12 @@ void SerialPort::open(std::string_view portName, const SerialConfig& config) { impl_->open(portName, config); } -void SerialPort::close() { - impl_->close(); +void SerialPort::close() { + impl_->close(); } -bool SerialPort::isOpen() const { - return impl_->isOpen(); +bool SerialPort::isOpen() const { + return impl_->isOpen(); } std::vector SerialPort::read(size_t maxBytes) { @@ -54,14 +54,14 @@ std::string SerialPort::readUntil(char terminator, std::chrono::milliseconds tim while (true) { const auto now = std::chrono::steady_clock::now(); const auto elapsed = std::chrono::duration_cast(now - startTime); - + if (elapsed >= timeout) { throw SerialTimeoutException("Waiting for terminator timed out"); } const auto remainingTime = timeout - elapsed; auto buffer = impl_->readExactly(1, remainingTime); - + if (buffer.empty()) { continue; } @@ -93,14 +93,14 @@ std::vector SerialPort::readUntilSequence(std::span sequ while (true) { const auto now = std::chrono::steady_clock::now(); const auto elapsed = std::chrono::duration_cast(now - startTime); - + if (elapsed >= timeout) { throw SerialTimeoutException("Waiting for termination sequence timed out"); } const auto remainingTime = timeout - elapsed; auto chunk = impl_->readExactly(1, remainingTime); - + if (chunk.empty()) { continue; } @@ -113,7 +113,7 @@ std::vector SerialPort::readUntilSequence(std::span sequ buffer.erase(buffer.begin()); } - if (buffer.size() == sequence.size() && + if (buffer.size() == sequence.size() && std::equal(buffer.begin(), buffer.end(), sequence.begin())) { if (!includeSequence) { result.erase(result.end() - static_cast(sequence.size()), result.end()); @@ -165,52 +165,52 @@ std::future SerialPort::asyncWrite(std::string_view data) { }); } -void SerialPort::flush() { - impl_->flush(); +void SerialPort::flush() { + impl_->flush(); } -void SerialPort::drain() { - impl_->drain(); +void SerialPort::drain() { + impl_->drain(); } -size_t SerialPort::available() const { - return impl_->available(); +size_t SerialPort::available() const { + return impl_->available(); } void SerialPort::setConfig(const SerialConfig& config) { impl_->setConfig(config); } -SerialConfig SerialPort::getConfig() const { - return impl_->getConfig(); +SerialConfig SerialPort::getConfig() const { + return impl_->getConfig(); } -void SerialPort::setDTR(bool value) { - impl_->setDTR(value); +void SerialPort::setDTR(bool value) { + impl_->setDTR(value); } -void SerialPort::setRTS(bool value) { - impl_->setRTS(value); +void SerialPort::setRTS(bool value) { + impl_->setRTS(value); } -bool SerialPort::getCTS() const { - return impl_->getCTS(); +bool SerialPort::getCTS() const { + return impl_->getCTS(); } -bool SerialPort::getDSR() const { - return impl_->getDSR(); +bool SerialPort::getDSR() const { + return impl_->getDSR(); } -bool SerialPort::getRI() const { - return impl_->getRI(); +bool SerialPort::getRI() const { + return impl_->getRI(); } -bool SerialPort::getCD() const { - return impl_->getCD(); +bool SerialPort::getCD() const { + return impl_->getCD(); } -std::string SerialPort::getPortName() const { - return impl_->getPortName(); +std::string SerialPort::getPortName() const { + return impl_->getPortName(); } std::vector SerialPort::getAvailablePorts() { @@ -226,4 +226,4 @@ std::optional SerialPort::tryOpen(std::string_view portName, const } } -} // namespace serial \ No newline at end of file +} // namespace serial diff --git a/atom/serial/serial_port.hpp b/atom/serial/serial_port.hpp index 3aa49055..ff28c69f 100644 --- a/atom/serial/serial_port.hpp +++ b/atom/serial/serial_port.hpp @@ -364,4 +364,4 @@ class SerialPort { std::unique_ptr impl_; }; -} // namespace serial \ No newline at end of file +} // namespace serial diff --git a/atom/serial/serial_port_unix.hpp b/atom/serial/serial_port_unix.hpp index d66f1478..e6568186 100644 --- a/atom/serial/serial_port_unix.hpp +++ b/atom/serial/serial_port_unix.hpp @@ -730,4 +730,4 @@ class SerialPortImpl { } // namespace serial -#endif // defined(__unix__) || defined(__APPLE__) \ No newline at end of file +#endif // defined(__unix__) || defined(__APPLE__) diff --git a/atom/serial/serial_port_win.hpp b/atom/serial/serial_port_win.hpp index 2bdd725f..b09e4d43 100644 --- a/atom/serial/serial_port_win.hpp +++ b/atom/serial/serial_port_win.hpp @@ -782,4 +782,4 @@ class SerialPortImpl { } // namespace serial -#endif // _WIN32 \ No newline at end of file +#endif // _WIN32 diff --git a/atom/serial/usb.hpp b/atom/serial/usb.hpp index e30cf7b6..399b4302 100644 --- a/atom/serial/usb.hpp +++ b/atom/serial/usb.hpp @@ -417,4 +417,4 @@ class UsbDevice { } // namespace atom::serial -#endif // ATOM_SERIAL_USB_HPP \ No newline at end of file +#endif // ATOM_SERIAL_USB_HPP diff --git a/atom/serial/xmake.lua b/atom/serial/xmake.lua index 895203a8..1e9fc253 100644 --- a/atom/serial/xmake.lua +++ b/atom/serial/xmake.lua @@ -35,18 +35,18 @@ local header_files = { -- Object Library target("atom-serial-object") set_kind("object") - + -- Add files add_files(table.unpack(source_files)) add_headerfiles(table.unpack(header_files)) - + -- Add dependencies add_packages("loguru") - + -- Add include directories add_includedirs(".", {public = true}) add_includedirs("..", {public = true}) - + -- Platform-specific settings if is_plat("windows") then add_defines("WIN32_LEAN_AND_MEAN") @@ -56,7 +56,7 @@ target("atom-serial-object") elseif is_plat("macosx") then add_frameworks("IOKit", "CoreFoundation") end - + -- Set C++ standard set_languages("c++20") target_end() @@ -65,11 +65,11 @@ target_end() target("atom-serial") -- Set library type based on parent project option set_kind(has_config("shared_libs") and "shared" or "static") - + -- Add dependencies add_deps("atom-serial-object") add_packages("loguru") - + -- Platform-specific settings if is_plat("windows") then add_syslinks("setupapi") @@ -78,11 +78,11 @@ target("atom-serial") elseif is_plat("macosx") then add_frameworks("IOKit", "CoreFoundation") end - + -- Set output directories set_targetdir("$(buildir)/lib") set_objectdir("$(buildir)/obj") - + -- Install configuration on_install(function (target) os.cp(target:targetfile(), path.join(target:installdir(), "lib")) diff --git a/atom/sysinfo/CMakeLists.txt b/atom/sysinfo/CMakeLists.txt index 3c1e687c..c85be2a3 100644 --- a/atom/sysinfo/CMakeLists.txt +++ b/atom/sysinfo/CMakeLists.txt @@ -84,4 +84,4 @@ install(TARGETS ${PROJECT_NAME} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} PUBLIC_HEADER DESTINATION include/${PROJECT_NAME} -) \ No newline at end of file +) diff --git a/atom/sysinfo/cpu.hpp b/atom/sysinfo/cpu.hpp index f17aa844..3172ffae 100644 --- a/atom/sysinfo/cpu.hpp +++ b/atom/sysinfo/cpu.hpp @@ -127,4 +127,4 @@ void refreshCpuInfo(); } // namespace atom::system -#endif // ATOM_SYSTEM_MODULE_CPU_HPP \ No newline at end of file +#endif // ATOM_SYSTEM_MODULE_CPU_HPP diff --git a/atom/sysinfo/cpu/freebsd.cpp b/atom/sysinfo/cpu/freebsd.cpp index a89290ef..4e557d56 100644 --- a/atom/sysinfo/cpu/freebsd.cpp +++ b/atom/sysinfo/cpu/freebsd.cpp @@ -28,197 +28,197 @@ auto getCPUModel_FreeBSD() -> std::string; auto getCurrentCpuUsage_FreeBSD() -> float { LOG_F(INFO, "Starting getCurrentCpuUsage function on FreeBSD"); - + static std::mutex mutex; static long lastTotal = 0, lastIdle = 0; - + std::unique_lock lock(mutex); - + float cpuUsage = 0.0f; - + long cp_time[CPUSTATES]; size_t len = sizeof(cp_time); - + if (sysctlbyname("kern.cp_time", &cp_time, &len, NULL, 0) != -1) { long total = cp_time[CP_USER] + cp_time[CP_NICE] + cp_time[CP_SYS] + cp_time[CP_IDLE] + cp_time[CP_INTR]; long idle = cp_time[CP_IDLE]; - + if (lastTotal > 0 && lastIdle > 0) { long totalDiff = total - lastTotal; long idleDiff = idle - lastIdle; - + if (totalDiff > 0) { cpuUsage = 100.0f * (1.0f - (static_cast(idleDiff) / totalDiff)); } } - + lastTotal = total; lastIdle = idle; } - + // Clamp to 0-100 range cpuUsage = std::max(0.0f, std::min(100.0f, cpuUsage)); - + LOG_F(INFO, "FreeBSD CPU Usage: {}%", cpuUsage); return cpuUsage; } auto getPerCoreCpuUsage() -> std::vector { LOG_F(INFO, "Starting getPerCoreCpuUsage function on FreeBSD"); - + static std::mutex mutex; static std::vector lastTotals; static std::vector lastIdles; - + std::unique_lock lock(mutex); - + int numCpus = getNumberOfLogicalCores(); std::vector coreUsages(numCpus, 0.0f); - + // Resize previous vectors if needed if (lastTotals.size() < static_cast(numCpus)) { lastTotals.resize(numCpus, 0); lastIdles.resize(numCpus, 0); } - + // Get per-CPU statistics for (int i = 0; i < numCpus; i++) { long cp_time[CPUSTATES]; size_t len = sizeof(cp_time); - + std::string sysctlName = "kern.cp_times"; if (sysctlbyname(sysctlName.c_str(), NULL, &len, NULL, 0) != -1) { std::vector times(len / sizeof(long)); if (sysctlbyname(sysctlName.c_str(), times.data(), &len, NULL, 0) != -1) { // Each CPU has CPUSTATES values int j = i * CPUSTATES; - long total = times[j + CP_USER] + times[j + CP_NICE] + times[j + CP_SYS] + + long total = times[j + CP_USER] + times[j + CP_NICE] + times[j + CP_SYS] + times[j + CP_IDLE] + times[j + CP_INTR]; long idle = times[j + CP_IDLE]; - + if (lastTotals[i] > 0 && lastIdles[i] > 0) { long totalDiff = total - lastTotals[i]; long idleDiff = idle - lastIdles[i]; - + if (totalDiff > 0) { coreUsages[i] = 100.0f * (1.0f - (static_cast(idleDiff) / totalDiff)); coreUsages[i] = std::max(0.0f, std::min(100.0f, coreUsages[i])); } } - + lastTotals[i] = total; lastIdles[i] = idle; } } } - + LOG_F(INFO, "FreeBSD Per-Core CPU Usage collected for {} cores", numCpus); return coreUsages; } auto getCurrentCpuTemperature() -> float { LOG_F(INFO, "Starting getCurrentCpuTemperature function on FreeBSD"); - + float temperature = 0.0f; - + // FreeBSD typically uses ACPI or hardware-specific drivers for temperature // This would require access to /dev/acpi or similar // This is a placeholder implementation - + LOG_F(INFO, "FreeBSD CPU Temperature: {}°C (placeholder)", temperature); return temperature; } auto getPerCoreCpuTemperature() -> std::vector { LOG_F(INFO, "Starting getPerCoreCpuTemperature function on FreeBSD"); - + int numCores = getNumberOfLogicalCores(); std::vector temperatures(numCores, 0.0f); - + // FreeBSD doesn't have a standard way to get per-core temperatures // This is a placeholder implementation - + LOG_F(INFO, "FreeBSD Per-Core CPU Temperature: placeholder values for {} cores", numCores); return temperatures; } auto getCPUModel() -> std::string { LOG_F(INFO, "Starting getCPUModel function on FreeBSD"); - + if (!needsCacheRefresh() && !g_cpuInfoCache.model.empty()) { return g_cpuInfoCache.model; } - + std::string cpuModel = "Unknown"; - + // Try to get model from sysctl char buffer[1024]; size_t len = sizeof(buffer); - + if (sysctlbyname("hw.model", buffer, &len, NULL, 0) != -1) { cpuModel = buffer; } - + LOG_F(INFO, "FreeBSD CPU Model: {}", cpuModel); return cpuModel; } auto getProcessorIdentifier() -> std::string { LOG_F(INFO, "Starting getProcessorIdentifier function on FreeBSD"); - + if (!needsCacheRefresh() && !g_cpuInfoCache.identifier.empty()) { return g_cpuInfoCache.identifier; } - + std::string identifier; - + // Combine hw.model with some additional CPU information char model[256]; size_t len = sizeof(model); - + if (sysctlbyname("hw.model", model, &len, NULL, 0) != -1) { identifier = model; - + // Try to get additional CPU information (family, level, etc.) int family = 0; len = sizeof(family); - + if (sysctlbyname("hw.cpu.family", &family, &len, NULL, 0) != -1) { identifier += " Family " + std::to_string(family); } - + int model_id = 0; len = sizeof(model_id); - + if (sysctlbyname("hw.cpu.model", &model_id, &len, NULL, 0) != -1) { identifier += " Model " + std::to_string(model_id); } - + int stepping = 0; len = sizeof(stepping); - + if (sysctlbyname("hw.cpu.stepping", &stepping, &len, NULL, 0) != -1) { identifier += " Stepping " + std::to_string(stepping); } } - + if (identifier.empty()) { identifier = "FreeBSD CPU"; } - + LOG_F(INFO, "FreeBSD CPU Identifier: {}", identifier); return identifier; } auto getProcessorFrequency() -> double { LOG_F(INFO, "Starting getProcessorFrequency function on FreeBSD"); - + double frequency = 0.0; - + // Try to get CPU frequency int freq = 0; size_t len = sizeof(freq); - + if (sysctlbyname("dev.cpu.0.freq", &freq, &len, NULL, 0) != -1) { // dev.cpu.0.freq returns frequency in MHz frequency = static_cast(freq) / 1000.0; // Convert MHz to GHz @@ -228,26 +228,26 @@ auto getProcessorFrequency() -> double { frequency = static_cast(freq) / 1000.0; // Convert MHz to GHz } } - + LOG_F(INFO, "FreeBSD CPU Frequency: {} GHz", frequency); return frequency; } auto getMinProcessorFrequency() -> double { LOG_F(INFO, "Starting getMinProcessorFrequency function on FreeBSD"); - + double minFreq = 0.0; - + // Check if CPU frequency scaling is available int freq = 0; size_t len = sizeof(freq); - + // Some FreeBSD systems expose this information if (sysctlbyname("dev.cpu.0.freq_levels", NULL, &len, NULL, 0) != -1) { std::vector freqLevels(len); if (sysctlbyname("dev.cpu.0.freq_levels", freqLevels.data(), &len, NULL, 0) != -1) { std::string levels(freqLevels.begin(), freqLevels.end()); - + // Format is typically "frequency/power frequency/power ..." // We want the lowest frequency size_t pos = levels.find_last_of(" \t"); @@ -264,7 +264,7 @@ auto getMinProcessorFrequency() -> double { } } } - + // Ensure we have a reasonable minimum value if (minFreq <= 0.0) { // As a fallback, estimate min as a fraction of current @@ -276,26 +276,26 @@ auto getMinProcessorFrequency() -> double { minFreq = 1.0; // Default fallback } } - + LOG_F(INFO, "FreeBSD CPU Min Frequency: {} GHz", minFreq); return minFreq; } auto getMaxProcessorFrequency() -> double { LOG_F(INFO, "Starting getMaxProcessorFrequency function on FreeBSD"); - + double maxFreq = 0.0; - + // Check if CPU frequency scaling is available int freq = 0; size_t len = sizeof(freq); - + // Some FreeBSD systems expose this information if (sysctlbyname("dev.cpu.0.freq_levels", NULL, &len, NULL, 0) != -1) { std::vector freqLevels(len); if (sysctlbyname("dev.cpu.0.freq_levels", freqLevels.data(), &len, NULL, 0) != -1) { std::string levels(freqLevels.begin(), freqLevels.end()); - + // Format is typically "frequency/power frequency/power ..." // We want the highest frequency (first one) size_t pos = levels.find('/'); @@ -308,30 +308,30 @@ auto getMaxProcessorFrequency() -> double { } } } - + // If we couldn't find a max frequency, use current as fallback if (maxFreq <= 0.0) { maxFreq = getProcessorFrequency(); LOG_F(INFO, "Using current CPU frequency as max: {} GHz", maxFreq); } - + LOG_F(INFO, "FreeBSD CPU Max Frequency: {} GHz", maxFreq); return maxFreq; } auto getPerCoreFrequencies() -> std::vector { LOG_F(INFO, "Starting getPerCoreFrequencies function on FreeBSD"); - + int numCores = getNumberOfLogicalCores(); std::vector frequencies(numCores, 0.0); - + // Try to get per-core frequencies for (int i = 0; i < numCores; i++) { std::string sysctlName = "dev.cpu." + std::to_string(i) + ".freq"; - + int freq = 0; size_t len = sizeof(freq); - + if (sysctlbyname(sysctlName.c_str(), &freq, &len, NULL, 0) != -1) { // dev.cpu.N.freq returns frequency in MHz frequencies[i] = static_cast(freq) / 1000.0; // Convert MHz to GHz @@ -344,248 +344,248 @@ auto getPerCoreFrequencies() -> std::vector { } } } - + LOG_F(INFO, "FreeBSD Per-Core CPU Frequencies collected for {} cores", numCores); return frequencies; } auto getNumberOfPhysicalPackages() -> int { LOG_F(INFO, "Starting getNumberOfPhysicalPackages function on FreeBSD"); - + if (!needsCacheRefresh() && g_cpuInfoCache.numPhysicalPackages > 0) { return g_cpuInfoCache.numPhysicalPackages; } - + // FreeBSD doesn't provide a direct way to get physical packages // Most systems have a single physical package int numberOfPackages = 1; - + // Check hw.packages if available int packages = 0; size_t len = sizeof(packages); - + if (sysctlbyname("hw.packages", &packages, &len, NULL, 0) != -1 && packages > 0) { numberOfPackages = packages; } - + LOG_F(INFO, "FreeBSD Physical CPU Packages: {}", numberOfPackages); return numberOfPackages; } auto getNumberOfPhysicalCores() -> int { LOG_F(INFO, "Starting getNumberOfPhysicalCores function on FreeBSD"); - + if (!needsCacheRefresh() && g_cpuInfoCache.numPhysicalCores > 0) { return g_cpuInfoCache.numPhysicalCores; } - + int numberOfCores = 0; - + // Try to get physical cores int physCores = 0; size_t len = sizeof(physCores); - + // Check hw.ncpu for physical cores if (sysctlbyname("hw.ncpu", &physCores, &len, NULL, 0) != -1) { numberOfCores = physCores; - + // Check if hyperthreading is enabled int hyperThreading = 0; len = sizeof(hyperThreading); - + if (sysctlbyname("hw.cpu_hyperthreading", &hyperThreading, &len, NULL, 0) != -1 && hyperThreading) { numberOfCores /= 2; // If hyperthreading is enabled, logical cores = 2 * physical cores } } - + // Ensure at least one core if (numberOfCores <= 0) { numberOfCores = 1; } - + LOG_F(INFO, "FreeBSD Physical CPU Cores: {}", numberOfCores); return numberOfCores; } auto getNumberOfLogicalCores() -> int { LOG_F(INFO, "Starting getNumberOfLogicalCores function on FreeBSD"); - + if (!needsCacheRefresh() && g_cpuInfoCache.numLogicalCores > 0) { return g_cpuInfoCache.numLogicalCores; } - + int numberOfCores = 0; - + // Get logical cores using hw.ncpu int ncpu = 0; size_t len = sizeof(ncpu); - + if (sysctlbyname("hw.ncpu", &ncpu, &len, NULL, 0) != -1) { numberOfCores = ncpu; } else { // Fall back to sysconf numberOfCores = static_cast(sysconf(_SC_NPROCESSORS_ONLN)); } - + // Ensure at least one core if (numberOfCores <= 0) { numberOfCores = 1; } - + LOG_F(INFO, "FreeBSD Logical CPU Cores: {}", numberOfCores); return numberOfCores; } auto getCacheSizes() -> CacheSizes { LOG_F(INFO, "Starting getCacheSizes function on FreeBSD"); - + if (!needsCacheRefresh() && (g_cpuInfoCache.caches.l1d > 0 || g_cpuInfoCache.caches.l2 > 0 || g_cpuInfoCache.caches.l3 > 0)) { return g_cpuInfoCache.caches; } - + CacheSizes cacheSizes{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - + // Try to read cache sizes int cachesize = 0; size_t len = sizeof(cachesize); - + // L1 Data Cache if (sysctlbyname("hw.l1dcachesize", &cachesize, &len, NULL, 0) != -1) { cacheSizes.l1d = static_cast(cachesize); } - + // L1 Instruction Cache if (sysctlbyname("hw.l1icachesize", &cachesize, &len, NULL, 0) != -1) { cacheSizes.l1i = static_cast(cachesize); } - + // L2 Cache if (sysctlbyname("hw.l2cachesize", &cachesize, &len, NULL, 0) != -1) { cacheSizes.l2 = static_cast(cachesize); } - + // L3 Cache if (sysctlbyname("hw.l3cachesize", &cachesize, &len, NULL, 0) != -1) { cacheSizes.l3 = static_cast(cachesize); } - + // Cache line sizes int lineSize = 0; - + if (sysctlbyname("hw.cacheline", &lineSize, &len, NULL, 0) != -1) { cacheSizes.l1d_line_size = lineSize; cacheSizes.l1i_line_size = lineSize; cacheSizes.l2_line_size = lineSize; cacheSizes.l3_line_size = lineSize; } - + LOG_F(INFO, "FreeBSD Cache Sizes: L1d={}KB, L1i={}KB, L2={}KB, L3={}KB", cacheSizes.l1d / 1024, cacheSizes.l1i / 1024, cacheSizes.l2 / 1024, cacheSizes.l3 / 1024); - + return cacheSizes; } auto getCpuLoadAverage() -> LoadAverage { LOG_F(INFO, "Starting getCpuLoadAverage function on FreeBSD"); - + LoadAverage loadAvg{0.0, 0.0, 0.0}; - + double avg[3]; if (getloadavg(avg, 3) == 3) { loadAvg.oneMinute = avg[0]; loadAvg.fiveMinutes = avg[1]; loadAvg.fifteenMinutes = avg[2]; } - + LOG_F(INFO, "FreeBSD Load Average: {}, {}, {}", loadAvg.oneMinute, loadAvg.fiveMinutes, loadAvg.fifteenMinutes); - + return loadAvg; } auto getCpuPowerInfo() -> CpuPowerInfo { LOG_F(INFO, "Starting getCpuPowerInfo function on FreeBSD"); - + CpuPowerInfo powerInfo{0.0, 0.0, 0.0}; - + // FreeBSD doesn't provide CPU power information through a simple API - + LOG_F(INFO, "FreeBSD CPU Power Info: Not implemented"); return powerInfo; } auto getCpuFeatureFlags() -> std::vector { LOG_F(INFO, "Starting getCpuFeatureFlags function on FreeBSD"); - + if (!needsCacheRefresh() && !g_cpuInfoCache.flags.empty()) { return g_cpuInfoCache.flags; } - + std::vector flags; - + // Get CPU feature flags char buffer[1024]; size_t len = sizeof(buffer); - + if (sysctlbyname("hw.cpu.features", buffer, &len, NULL, 0) != -1) { std::string flagsStr(buffer); std::istringstream ss(flagsStr); std::string flag; - + while (ss >> flag) { flags.push_back(flag); } } - + // Additional features for newer CPUs if (sysctlbyname("hw.cpu.features.ext", buffer, &len, NULL, 0) != -1) { std::string flagsStr(buffer); std::istringstream ss(flagsStr); std::string flag; - + while (ss >> flag) { flags.push_back(flag); } } - + // Even more features if (sysctlbyname("hw.cpu.features.amd", buffer, &len, NULL, 0) != -1) { std::string flagsStr(buffer); std::istringstream ss(flagsStr); std::string flag; - + while (ss >> flag) { flags.push_back(flag); } } - + // Remove duplicates std::sort(flags.begin(), flags.end()); flags.erase(std::unique(flags.begin(), flags.end()), flags.end()); - + LOG_F(INFO, "FreeBSD CPU Flags: {} features collected", flags.size()); return flags; } auto getCpuArchitecture() -> CpuArchitecture { LOG_F(INFO, "Starting getCpuArchitecture function on FreeBSD"); - + if (!needsCacheRefresh()) { std::lock_guard lock(g_cacheMutex); if (g_cacheInitialized && g_cpuInfoCache.architecture != CpuArchitecture::UNKNOWN) { return g_cpuInfoCache.architecture; } } - + CpuArchitecture arch = CpuArchitecture::UNKNOWN; - + // Get architecture using uname struct utsname sysInfo; if (uname(&sysInfo) == 0) { std::string machine = sysInfo.machine; - + if (machine == "amd64") { arch = CpuArchitecture::X86_64; } else if (machine == "i386") { @@ -602,57 +602,57 @@ auto getCpuArchitecture() -> CpuArchitecture { arch = CpuArchitecture::RISC_V; } } - + LOG_F(INFO, "FreeBSD CPU Architecture: {}", cpuArchitectureToString(arch)); return arch; } auto getCpuVendor() -> CpuVendor { LOG_F(INFO, "Starting getCpuVendor function on FreeBSD"); - + if (!needsCacheRefresh()) { std::lock_guard lock(g_cacheMutex); if (g_cacheInitialized && g_cpuInfoCache.vendor != CpuVendor::UNKNOWN) { return g_cpuInfoCache.vendor; } } - + CpuVendor vendor = CpuVendor::UNKNOWN; std::string vendorString; - + char buffer[64]; size_t len = sizeof(buffer); - + if (sysctlbyname("hw.cpu.vendor", buffer, &len, NULL, 0) != -1) { vendorString = buffer; } - + vendor = getVendorFromString(vendorString); - + LOG_F(INFO, "FreeBSD CPU Vendor: {} ({})", vendorString, cpuVendorToString(vendor)); return vendor; } auto getCpuSocketType() -> std::string { LOG_F(INFO, "Starting getCpuSocketType function on FreeBSD"); - + if (!needsCacheRefresh() && !g_cpuInfoCache.socketType.empty()) { return g_cpuInfoCache.socketType; } - + std::string socketType = "Unknown"; - + // FreeBSD doesn't provide socket type directly - + LOG_F(INFO, "FreeBSD CPU Socket Type: {} (placeholder)", socketType); return socketType; } auto getCpuScalingGovernor() -> std::string { LOG_F(INFO, "Starting getCpuScalingGovernor function on FreeBSD"); - + std::string governor = "Unknown"; - + // Check if powerd is running FILE* pipe = popen("service powerd status", "r"); if (pipe) { @@ -664,12 +664,12 @@ auto getCpuScalingGovernor() -> std::string { } pclose(pipe); } - + // Check the current governor setting if (governor == "powerd") { int economy = 0, performance = 0; size_t len = sizeof(economy); - + if (sysctlbyname("hw.acpi.cpu.px_dom0.select", &economy, &len, NULL, 0) != -1) { if (economy == 0) { governor = "performance"; @@ -678,24 +678,24 @@ auto getCpuScalingGovernor() -> std::string { } } } - + LOG_F(INFO, "FreeBSD CPU Scaling Governor: {}", governor); return governor; } auto getPerCoreScalingGovernors() -> std::vector { LOG_F(INFO, "Starting getPerCoreScalingGovernors function on FreeBSD"); - + int numCores = getNumberOfLogicalCores(); std::vector governors(numCores); - + // FreeBSD typically uses the same governor for all cores std::string governor = getCpuScalingGovernor(); - + for (int i = 0; i < numCores; ++i) { governors[i] = governor; } - + LOG_F(INFO, "FreeBSD Per-Core Scaling Governors: {} (same for all cores)", governor); return governors; } diff --git a/atom/sysinfo/cpu/macos.cpp b/atom/sysinfo/cpu/macos.cpp index 78cbf9a2..11d363e6 100644 --- a/atom/sysinfo/cpu/macos.cpp +++ b/atom/sysinfo/cpu/macos.cpp @@ -28,118 +28,118 @@ auto getCPUModel_MacOS() -> std::string; auto getCurrentCpuUsage_MacOS() -> float { LOG_F(INFO, "Starting getCurrentCpuUsage function on macOS"); - + processor_cpu_load_info_t cpuInfo; mach_msg_type_number_t count; - + float cpuUsage = 0.0F; - - if (host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &count, + + if (host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &count, reinterpret_cast(&cpuInfo), &count) == KERN_SUCCESS) { - + static unsigned long long previousUser = 0, previousSystem = 0, previousIdle = 0; - + unsigned long long user = 0, system = 0, idle = 0; - + // Sum usage across all CPUs for (unsigned i = 0; i < count / CPU_STATE_MAX; i++) { user += cpuInfo[i].cpu_ticks[CPU_STATE_USER] + cpuInfo[i].cpu_ticks[CPU_STATE_NICE]; system += cpuInfo[i].cpu_ticks[CPU_STATE_SYSTEM]; idle += cpuInfo[i].cpu_ticks[CPU_STATE_IDLE]; } - + if (previousUser > 0 || previousSystem > 0 || previousIdle > 0) { unsigned long long userDiff = user - previousUser; unsigned long long systemDiff = system - previousSystem; unsigned long long idleDiff = idle - previousIdle; - + unsigned long long totalTicks = userDiff + systemDiff + idleDiff; - + if (totalTicks > 0) { cpuUsage = 100.0F * (static_cast(userDiff + systemDiff) / totalTicks); } } - + previousUser = user; previousSystem = system; previousIdle = idle; - + // Free the allocated memory vm_deallocate(mach_task_self(), reinterpret_cast(cpuInfo), count); } - + // Clamp to 0-100 range cpuUsage = std::max(0.0F, std::min(100.0F, cpuUsage)); - + LOG_F(INFO, "macOS CPU Usage: {}%", cpuUsage); return cpuUsage; } auto getPerCoreCpuUsage() -> std::vector { LOG_F(INFO, "Starting getPerCoreCpuUsage function on macOS"); - + processor_cpu_load_info_t cpuInfo; mach_msg_type_number_t count; - + std::vector coreUsages; - - if (host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &count, + + if (host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &count, reinterpret_cast(&cpuInfo), &count) == KERN_SUCCESS) { - + static std::vector previousUser, previousSystem, previousIdle; - + int numCores = count / CPU_STATE_MAX; coreUsages.resize(numCores, 0.0F); - + // Resize previous vectors if needed if (previousUser.size() < static_cast(numCores)) { previousUser.resize(numCores, 0); previousSystem.resize(numCores, 0); previousIdle.resize(numCores, 0); } - + for (int i = 0; i < numCores; i++) { unsigned long long user = cpuInfo[i].cpu_ticks[CPU_STATE_USER] + cpuInfo[i].cpu_ticks[CPU_STATE_NICE]; unsigned long long system = cpuInfo[i].cpu_ticks[CPU_STATE_SYSTEM]; unsigned long long idle = cpuInfo[i].cpu_ticks[CPU_STATE_IDLE]; - + if (previousUser[i] > 0 || previousSystem[i] > 0 || previousIdle[i] > 0) { unsigned long long userDiff = user - previousUser[i]; unsigned long long systemDiff = system - previousSystem[i]; unsigned long long idleDiff = idle - previousIdle[i]; - + unsigned long long totalTicks = userDiff + systemDiff + idleDiff; - + if (totalTicks > 0) { coreUsages[i] = 100.0F * (static_cast(userDiff + systemDiff) / totalTicks); coreUsages[i] = std::max(0.0F, std::min(100.0F, coreUsages[i])); } } - + previousUser[i] = user; previousSystem[i] = system; previousIdle[i] = idle; } - + // Free the allocated memory vm_deallocate(mach_task_self(), reinterpret_cast(cpuInfo), count); } - + LOG_F(INFO, "macOS Per-Core CPU Usage collected for {} cores", coreUsages.size()); return coreUsages; } auto getCurrentCpuTemperature() -> float { LOG_F(INFO, "Starting getCurrentCpuTemperature function on macOS"); - + // macOS doesn't provide a direct API for CPU temperature // This would require SMC (System Management Controller) access // through a third-party library like SMCKit - + float temperature = 0.0F; - + // This is a placeholder implementation LOG_F(INFO, "macOS CPU Temperature: {}°C (not implemented)", temperature); return temperature; @@ -147,41 +147,41 @@ auto getCurrentCpuTemperature() -> float { auto getPerCoreCpuTemperature() -> std::vector { LOG_F(INFO, "Starting getPerCoreCpuTemperature function on macOS"); - + int numCores = getNumberOfLogicalCores(); std::vector temperatures(numCores, 0.0F); - + // macOS doesn't provide per-core temperatures through a public API // This is a placeholder implementation - + LOG_F(INFO, "macOS Per-Core CPU Temperature: not implemented, returning zeros for {} cores", numCores); return temperatures; } auto getCPUModel() -> std::string { LOG_F(INFO, "Starting getCPUModel function on macOS"); - + if (!needsCacheRefresh() && !g_cpuInfoCache.model.empty()) { return g_cpuInfoCache.model; } - + std::string cpuModel = "Unknown"; - + // Use sysctl to get CPU model char buffer[1024]; size_t bufferSize = sizeof(buffer); - + if (sysctlbyname("machdep.cpu.brand_string", buffer, &bufferSize, NULL, 0) == 0) { cpuModel = buffer; } else { // For Apple Silicon, get chip name if (sysctlbyname("machdep.cpu.brand", buffer, &bufferSize, NULL, 0) == 0) { cpuModel = buffer; - + // Try to get more information for Apple Silicon char modelBuffer[256]; size_t modelBufferSize = sizeof(modelBuffer); - + if (sysctlbyname("hw.model", modelBuffer, &modelBufferSize, NULL, 0) == 0) { if (std::string(modelBuffer).find("Mac") != std::string::npos) { cpuModel += " " + std::string(modelBuffer); @@ -189,35 +189,35 @@ auto getCPUModel() -> std::string { } } } - + LOG_F(INFO, "macOS CPU Model: {}", cpuModel); return cpuModel; } auto getProcessorIdentifier() -> std::string { LOG_F(INFO, "Starting getProcessorIdentifier function on macOS"); - + if (!needsCacheRefresh() && !g_cpuInfoCache.identifier.empty()) { return g_cpuInfoCache.identifier; } - + std::string identifier = "Unknown"; - + // Get CPU vendor, family, model, and stepping char vendor[64]; int family = 0, model = 0, stepping = 0; size_t size = sizeof(vendor); - + if (sysctlbyname("machdep.cpu.vendor", vendor, &size, NULL, 0) == 0) { size = sizeof(family); sysctlbyname("machdep.cpu.family", &family, &size, NULL, 0); - + size = sizeof(model); sysctlbyname("machdep.cpu.model", &model, &size, NULL, 0); - + size = sizeof(stepping); sysctlbyname("machdep.cpu.stepping", &stepping, &size, NULL, 0); - + identifier = std::string(vendor) + " Family " + std::to_string(family) + " Model " + std::to_string(model) + " Stepping " + std::to_string(stepping); @@ -225,24 +225,24 @@ auto getProcessorIdentifier() -> std::string { // For Apple Silicon, use what we can get char buffer[256]; size = sizeof(buffer); - + if (sysctlbyname("machdep.cpu.brand", buffer, &size, NULL, 0) == 0) { identifier = buffer; } } - + LOG_F(INFO, "macOS CPU Identifier: {}", identifier); return identifier; } auto getProcessorFrequency() -> double { LOG_F(INFO, "Starting getProcessorFrequency function on macOS"); - + double frequency = 0.0; - + uint64_t freq = 0; size_t size = sizeof(freq); - + // Try to get the CPU frequency if (sysctlbyname("hw.cpufrequency", &freq, &size, NULL, 0) == 0) { frequency = static_cast(freq) / 1000000000.0; // Convert Hz to GHz @@ -250,31 +250,31 @@ auto getProcessorFrequency() -> double { // Try CPU frequency in MHz (some older Macs) unsigned int freqMHz = 0; size = sizeof(freqMHz); - + if (sysctlbyname("hw.cpufrequency_max", &freq, &size, NULL, 0) == 0) { frequency = static_cast(freq) / 1000000000.0; // Convert Hz to GHz } else if (sysctlbyname("hw.cpufrequency_max", &freqMHz, &size, NULL, 0) == 0) { frequency = static_cast(freqMHz) / 1000.0; // Convert MHz to GHz } } - + LOG_F(INFO, "macOS CPU Frequency: {} GHz", frequency); return frequency; } auto getMinProcessorFrequency() -> double { LOG_F(INFO, "Starting getMinProcessorFrequency function on macOS"); - + double minFreq = 0.0; - + // Try to get the minimum CPU frequency uint64_t freq = 0; size_t size = sizeof(freq); - + if (sysctlbyname("hw.cpufrequency_min", &freq, &size, NULL, 0) == 0) { minFreq = static_cast(freq) / 1000000000.0; // Convert Hz to GHz } - + // Ensure we have a reasonable minimum value if (minFreq <= 0.0) { // As a fallback, estimate min as a fraction of current @@ -286,20 +286,20 @@ auto getMinProcessorFrequency() -> double { minFreq = 1.0; // Default fallback } } - + LOG_F(INFO, "macOS CPU Min Frequency: {} GHz", minFreq); return minFreq; } auto getMaxProcessorFrequency() -> double { LOG_F(INFO, "Starting getMaxProcessorFrequency function on macOS"); - + double maxFreq = 0.0; - + // Try to get the maximum CPU frequency uint64_t freq = 0; size_t size = sizeof(freq); - + if (sysctlbyname("hw.cpufrequency_max", &freq, &size, NULL, 0) == 0) { maxFreq = static_cast(freq) / 1000000000.0; // Convert Hz to GHz } else { @@ -308,91 +308,91 @@ auto getMaxProcessorFrequency() -> double { maxFreq = static_cast(freq) / 1000000000.0; // Convert Hz to GHz } } - + // If still no valid max frequency, use current as fallback if (maxFreq <= 0.0) { maxFreq = getProcessorFrequency(); LOG_F(INFO, "Using current CPU frequency as max: {} GHz", maxFreq); } - + LOG_F(INFO, "macOS CPU Max Frequency: {} GHz", maxFreq); return maxFreq; } auto getPerCoreFrequencies() -> std::vector { LOG_F(INFO, "Starting getPerCoreFrequencies function on macOS"); - + int numCores = getNumberOfLogicalCores(); std::vector frequencies(numCores, 0.0); - + // macOS doesn't provide per-core frequencies through a simple API // Use the overall CPU frequency for all cores double frequency = getProcessorFrequency(); - + for (int i = 0; i < numCores; i++) { frequencies[i] = frequency; } - + LOG_F(INFO, "macOS Per-Core CPU Frequencies: {} GHz (all cores)", frequency); return frequencies; } auto getNumberOfPhysicalPackages() -> int { LOG_F(INFO, "Starting getNumberOfPhysicalPackages function on macOS"); - + if (!needsCacheRefresh() && g_cpuInfoCache.numPhysicalPackages > 0) { return g_cpuInfoCache.numPhysicalPackages; } - + // Most Macs have a single physical CPU package int numberOfPackages = 1; - + LOG_F(INFO, "macOS Physical CPU Packages: {}", numberOfPackages); return numberOfPackages; } auto getNumberOfPhysicalCores() -> int { LOG_F(INFO, "Starting getNumberOfPhysicalCores function on macOS"); - + if (!needsCacheRefresh() && g_cpuInfoCache.numPhysicalCores > 0) { return g_cpuInfoCache.numPhysicalCores; } - + int numberOfCores = 0; - + // Get physical cores int physCores = 0; size_t size = sizeof(physCores); - + if (sysctlbyname("hw.physicalcpu", &physCores, &size, NULL, 0) == 0) { numberOfCores = physCores; } else { // Fall back to logical cores and account for hyperthreading numberOfCores = getNumberOfLogicalCores() / 2; } - + // Ensure at least one core if (numberOfCores <= 0) { numberOfCores = 1; } - + LOG_F(INFO, "macOS Physical CPU Cores: {}", numberOfCores); return numberOfCores; } auto getNumberOfLogicalCores() -> int { LOG_F(INFO, "Starting getNumberOfLogicalCores function on macOS"); - + if (!needsCacheRefresh() && g_cpuInfoCache.numLogicalCores > 0) { return g_cpuInfoCache.numLogicalCores; } - + int numberOfCores = 0; - + // Get logical cores int logicalCores = 0; size_t size = sizeof(logicalCores); - + if (sysctlbyname("hw.logicalcpu", &logicalCores, &size, NULL, 0) == 0) { numberOfCores = logicalCores; } else { @@ -404,117 +404,117 @@ auto getNumberOfLogicalCores() -> int { numberOfCores = static_cast(sysconf(_SC_NPROCESSORS_ONLN)); } } - + // Ensure at least one core if (numberOfCores <= 0) { numberOfCores = 1; } - + LOG_F(INFO, "macOS Logical CPU Cores: {}", numberOfCores); return numberOfCores; } auto getCacheSizes() -> CacheSizes { LOG_F(INFO, "Starting getCacheSizes function on macOS"); - + if (!needsCacheRefresh() && (g_cpuInfoCache.caches.l1d > 0 || g_cpuInfoCache.caches.l2 > 0 || g_cpuInfoCache.caches.l3 > 0)) { return g_cpuInfoCache.caches; } - + CacheSizes cacheSizes{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - + // Read cache sizes from sysctl uint64_t cacheSize = 0; size_t size = sizeof(cacheSize); - + // L1 Data Cache if (sysctlbyname("hw.l1dcachesize", &cacheSize, &size, NULL, 0) == 0) { cacheSizes.l1d = static_cast(cacheSize); } - + // L1 Instruction Cache if (sysctlbyname("hw.l1icachesize", &cacheSize, &size, NULL, 0) == 0) { cacheSizes.l1i = static_cast(cacheSize); } - + // L2 Cache if (sysctlbyname("hw.l2cachesize", &cacheSize, &size, NULL, 0) == 0) { cacheSizes.l2 = static_cast(cacheSize); } - + // L3 Cache if (sysctlbyname("hw.l3cachesize", &cacheSize, &size, NULL, 0) == 0) { cacheSizes.l3 = static_cast(cacheSize); } - + // Get line sizes and associativity if available int lineSize = 0; size = sizeof(lineSize); - + if (sysctlbyname("hw.cachelinesize", &lineSize, &size, NULL, 0) == 0) { cacheSizes.l1d_line_size = lineSize; cacheSizes.l1i_line_size = lineSize; cacheSizes.l2_line_size = lineSize; cacheSizes.l3_line_size = lineSize; } - + int l2associativity = 0; size = sizeof(l2associativity); if (sysctlbyname("machdep.cpu.cache.L2_associativity", &l2associativity, &size, NULL, 0) == 0) { cacheSizes.l2_associativity = l2associativity; } - + LOG_F(INFO, "macOS Cache Sizes: L1d={}KB, L1i={}KB, L2={}KB, L3={}KB", cacheSizes.l1d / 1024, cacheSizes.l1i / 1024, cacheSizes.l2 / 1024, cacheSizes.l3 / 1024); - + return cacheSizes; } auto getCpuLoadAverage() -> LoadAverage { LOG_F(INFO, "Starting getCpuLoadAverage function on macOS"); - + LoadAverage loadAvg{0.0, 0.0, 0.0}; - + double avg[3]; if (getloadavg(avg, 3) == 3) { loadAvg.oneMinute = avg[0]; loadAvg.fiveMinutes = avg[1]; loadAvg.fifteenMinutes = avg[2]; } - + LOG_F(INFO, "macOS Load Average: {}, {}, {}", loadAvg.oneMinute, loadAvg.fiveMinutes, loadAvg.fifteenMinutes); - + return loadAvg; } auto getCpuPowerInfo() -> CpuPowerInfo { LOG_F(INFO, "Starting getCpuPowerInfo function on macOS"); - + CpuPowerInfo powerInfo{0.0, 0.0, 0.0}; - + // macOS doesn't provide this information through a public API - + LOG_F(INFO, "macOS CPU Power Info: Not implemented"); return powerInfo; } auto getCpuFeatureFlags() -> std::vector { LOG_F(INFO, "Starting getCpuFeatureFlags function on macOS"); - + if (!needsCacheRefresh() && !g_cpuInfoCache.flags.empty()) { return g_cpuInfoCache.flags; } - + std::vector flags; - + // Check for common flags using sysctlbyname auto checkFeature = [&flags](const char* name) { int supported = 0; size_t size = sizeof(supported); - + if (sysctlbyname(name, &supported, &size, NULL, 0) == 0 && supported) { // Extract feature name from sysctl name std::string featureName = name; @@ -524,7 +524,7 @@ auto getCpuFeatureFlags() -> std::vector { } } }; - + // Intel CPU features checkFeature("hw.optional.floatingpoint"); checkFeature("hw.optional.mmx"); @@ -549,7 +549,7 @@ auto getCpuFeatureFlags() -> std::vector { checkFeature("hw.optional.avx512vl"); checkFeature("hw.optional.avx512ifma"); checkFeature("hw.optional.avx512vbmi"); - + // ARM features checkFeature("hw.optional.neon"); checkFeature("hw.optional.armv8_1_atomics"); @@ -558,23 +558,23 @@ auto getCpuFeatureFlags() -> std::vector { checkFeature("hw.optional.armv8_2_sha3"); checkFeature("hw.optional.amx_version"); checkFeature("hw.optional.ucnormal_mem"); - + LOG_F(INFO, "macOS CPU Flags: {} features collected", flags.size()); return flags; } auto getCpuArchitecture() -> CpuArchitecture { LOG_F(INFO, "Starting getCpuArchitecture function on macOS"); - + if (!needsCacheRefresh()) { std::lock_guard lock(g_cacheMutex); if (g_cacheInitialized && g_cpuInfoCache.architecture != CpuArchitecture::UNKNOWN) { return g_cpuInfoCache.architecture; } } - + CpuArchitecture arch = CpuArchitecture::UNKNOWN; - + #ifdef __x86_64__ arch = CpuArchitecture::X86_64; #elif defined(__i386__) @@ -588,7 +588,7 @@ auto getCpuArchitecture() -> CpuArchitecture { struct utsname sysInfo; if (uname(&sysInfo) == 0) { std::string machine = sysInfo.machine; - + if (machine == "x86_64") { arch = CpuArchitecture::X86_64; } else if (machine == "i386") { @@ -600,27 +600,27 @@ auto getCpuArchitecture() -> CpuArchitecture { } } #endif - + LOG_F(INFO, "macOS CPU Architecture: {}", cpuArchitectureToString(arch)); return arch; } auto getCpuVendor() -> CpuVendor { LOG_F(INFO, "Starting getCpuVendor function on macOS"); - + if (!needsCacheRefresh()) { std::lock_guard lock(g_cacheMutex); if (g_cacheInitialized && g_cpuInfoCache.vendor != CpuVendor::UNKNOWN) { return g_cpuInfoCache.vendor; } } - + CpuVendor vendor = CpuVendor::UNKNOWN; std::string vendorString = "Unknown"; - + char buffer[64]; size_t size = sizeof(buffer); - + if (sysctlbyname("machdep.cpu.vendor", buffer, &size, NULL, 0) == 0) { vendorString = buffer; } else { @@ -630,48 +630,48 @@ auto getCpuVendor() -> CpuVendor { vendorString = "Apple"; } } - + vendor = getVendorFromString(vendorString); - + LOG_F(INFO, "macOS CPU Vendor: {} ({})", vendorString, cpuVendorToString(vendor)); return vendor; } auto getCpuSocketType() -> std::string { LOG_F(INFO, "Starting getCpuSocketType function on macOS"); - + if (!needsCacheRefresh() && !g_cpuInfoCache.socketType.empty()) { return g_cpuInfoCache.socketType; } - + std::string socketType = "Unknown"; - + // Check architecture to determine socket type CpuArchitecture arch = getCpuArchitecture(); - + if (arch == CpuArchitecture::ARM64 || arch == CpuArchitecture::ARM) { socketType = "Apple SoC"; } else { // For Intel Macs, socket type is generally not available through public APIs socketType = "Intel Mac"; } - + LOG_F(INFO, "macOS CPU Socket Type: {}", socketType); return socketType; } auto getCpuScalingGovernor() -> std::string { LOG_F(INFO, "Starting getCpuScalingGovernor function on macOS"); - + std::string governor = "Unknown"; - + // Get power management mode // This is a simplified approach - macOS uses more sophisticated power management - + // Check if we can get power management information int perfMode = 0; size_t size = sizeof(perfMode); - + if (sysctlbyname("hw.perflevel0.frequency", &perfMode, &size, NULL, 0) == 0) { governor = "perflevel"; } else { @@ -679,37 +679,37 @@ auto getCpuScalingGovernor() -> std::string { CFTypeRef powerSourceInfo = IOPSCopyPowerSourcesInfo(); if (powerSourceInfo) { CFArrayRef powerSources = IOPSCopyPowerSourcesList(powerSourceInfo); - + if (powerSources && CFArrayGetCount(powerSources) > 0) { CFDictionaryRef powerSource = (CFDictionaryRef)CFArrayGetValueAtIndex(powerSources, 0); CFStringRef powerSourceState = (CFStringRef)CFDictionaryGetValue(powerSource, CFSTR(kIOPSPowerSourceStateKey)); - + if (powerSourceState) { bool onBattery = CFStringCompare(powerSourceState, CFSTR(kIOPSBatteryPowerValue), 0) == kCFCompareEqualTo; governor = onBattery ? "Battery Power" : "AC Power"; } } - + if (powerSources) { CFRelease(powerSources); } CFRelease(powerSourceInfo); } } - + LOG_F(INFO, "macOS CPU Power Mode: {}", governor); return governor; } auto getPerCoreScalingGovernors() -> std::vector { LOG_F(INFO, "Starting getPerCoreScalingGovernors function on macOS"); - + int numCores = getNumberOfLogicalCores(); std::string governor = getCpuScalingGovernor(); - + // macOS uses a system-wide power management policy std::vector governors(numCores, governor); - + LOG_F(INFO, "macOS Per-Core Power Modes: {} (same for all cores)", governor); return governors; } diff --git a/atom/sysinfo/disk.hpp b/atom/sysinfo/disk.hpp index d1998e76..d750b6c2 100644 --- a/atom/sysinfo/disk.hpp +++ b/atom/sysinfo/disk.hpp @@ -17,7 +17,7 @@ Description: System Information Module - Disk /** * @brief Disk module for system information - * + * * This module provides functionality for retrieving disk information, * monitoring disk events, and managing disk security. */ @@ -30,4 +30,4 @@ Description: System Information Module - Disk #include "atom/sysinfo/disk/disk_security.hpp" #include "atom/sysinfo/disk/disk_monitor.hpp" -#endif // ATOM_SYSTEM_MODULE_DISK_HPP \ No newline at end of file +#endif // ATOM_SYSTEM_MODULE_DISK_HPP diff --git a/atom/sysinfo/disk/disk_monitor.cpp b/atom/sysinfo/disk/disk_monitor.cpp index 59a50ada..de8293ad 100644 --- a/atom/sysinfo/disk/disk_monitor.cpp +++ b/atom/sysinfo/disk/disk_monitor.cpp @@ -197,11 +197,11 @@ std::future startDeviceMonitoring(std::function:windows.hpp> diff --git a/atom/sysinfo/memory/windows.cpp b/atom/sysinfo/memory/windows.cpp index 214316a9..3f7345bd 100644 --- a/atom/sysinfo/memory/windows.cpp +++ b/atom/sysinfo/memory/windows.cpp @@ -34,21 +34,21 @@ constexpr int MEMORY_TEST_SIZE = 1024 * 1024; static MEMORYSTATUSEX getMemoryStatus() { MEMORYSTATUSEX status{}; status.dwLength = sizeof(status); - + if (!GlobalMemoryStatusEx(&status)) { spdlog::error("Failed to get memory status: {}", GetLastError()); } - + return status; } static PROCESS_MEMORY_COUNTERS getProcessMemoryCounters() { PROCESS_MEMORY_COUNTERS pmc{}; - + if (!GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) { spdlog::error("Failed to get process memory info: {}", GetLastError()); } - + return pmc; } } @@ -78,7 +78,7 @@ auto getTotalMemorySize() -> unsigned long long { if (status.ullTotalPhys > 0) { spdlog::debug("Total memory size: {} bytes", status.ullTotalPhys); } - + return status.ullTotalPhys; } @@ -89,7 +89,7 @@ auto getAvailableMemorySize() -> unsigned long long { if (status.ullAvailPhys > 0) { spdlog::debug("Available memory size: {} bytes", status.ullAvailPhys); } - + return status.ullAvailPhys; } @@ -98,7 +98,7 @@ auto getPhysicalMemoryInfo() -> MemoryInfo::MemorySlot { MemoryInfo::MemorySlot slot; const auto status = getMemoryStatus(); - + if (status.ullTotalPhys > 0) { slot.capacity = std::to_string(status.ullTotalPhys / (1024 * 1024)); slot.type = "DDR"; @@ -116,7 +116,7 @@ auto getVirtualMemoryMax() -> unsigned long long { if (status.ullTotalVirtual > 0) { spdlog::debug("Maximum virtual memory: {} bytes", status.ullTotalVirtual); } - + return status.ullTotalVirtual; } @@ -129,7 +129,7 @@ auto getVirtualMemoryUsed() -> unsigned long long { spdlog::debug("Used virtual memory: {} bytes", usedVirtual); return usedVirtual; } - + return 0; } @@ -140,7 +140,7 @@ auto getSwapMemoryTotal() -> unsigned long long { if (status.ullTotalPageFile > 0) { spdlog::debug("Total swap memory: {} bytes", status.ullTotalPageFile); } - + return status.ullTotalPageFile; } @@ -153,7 +153,7 @@ auto getSwapMemoryUsed() -> unsigned long long { spdlog::debug("Used swap memory: {} bytes", usedSwap); return usedSwap; } - + return 0; } @@ -166,7 +166,7 @@ auto getCommittedMemory() -> size_t { spdlog::debug("Committed memory: {} bytes", committed); return static_cast(committed); } - + return 0; } @@ -178,7 +178,7 @@ auto getUncommittedMemory() -> size_t { spdlog::debug("Uncommitted memory: {} bytes", status.ullAvailPhys); return static_cast(status.ullAvailPhys); } - + return 0; } @@ -187,7 +187,7 @@ auto getDetailedMemoryStats() -> MemoryInfo { MemoryInfo info{}; const auto memStatus = getMemoryStatus(); - + if (memStatus.ullTotalPhys > 0) { info.memoryLoadPercentage = memStatus.dwMemoryLoad; info.totalPhysicalMemory = memStatus.ullTotalPhys; @@ -226,7 +226,7 @@ auto getPeakWorkingSetSize() -> size_t { if (pmc.PeakWorkingSetSize > 0) { spdlog::debug("Peak working set size: {} bytes", pmc.PeakWorkingSetSize); } - + return pmc.PeakWorkingSetSize; } @@ -237,7 +237,7 @@ auto getCurrentWorkingSetSize() -> size_t { if (pmc.WorkingSetSize > 0) { spdlog::debug("Current working set size: {} bytes", pmc.WorkingSetSize); } - + return pmc.WorkingSetSize; } @@ -246,7 +246,7 @@ auto getPageFaultCount() -> size_t { const auto pmc = getProcessMemoryCounters(); spdlog::debug("Page fault count: {}", pmc.PageFaultCount); - + return pmc.PageFaultCount; } @@ -255,11 +255,11 @@ auto getMemoryLoadPercentage() -> double { const auto status = getMemoryStatus(); const auto memoryLoad = static_cast(status.dwMemoryLoad); - + if (status.ullTotalPhys > 0) { spdlog::debug("Memory load: {}%", memoryLoad); } - + return memoryLoad; } @@ -275,7 +275,7 @@ auto getMemoryPerformance() -> MemoryPerformance { if (PdhOpenQuery(nullptr, 0, &query) == ERROR_SUCCESS) { const auto addCounterResult1 = PdhAddCounterW(query, L"\\Memory\\Pages/sec", 0, &readCounter); const auto addCounterResult2 = PdhAddCounterW(query, L"\\Memory\\Page Writes/sec", 0, &writeCounter); - + if (addCounterResult1 == ERROR_SUCCESS && addCounterResult2 == ERROR_SUCCESS) { PdhCollectQueryData(query); std::this_thread::sleep_for(std::chrono::seconds(1)); @@ -283,10 +283,10 @@ auto getMemoryPerformance() -> MemoryPerformance { PDH_FMT_COUNTERVALUE readValue{}; PDH_FMT_COUNTERVALUE writeValue{}; - + const auto getValueResult1 = PdhGetFormattedCounterValue(readCounter, PDH_FMT_DOUBLE, nullptr, &readValue); const auto getValueResult2 = PdhGetFormattedCounterValue(writeCounter, PDH_FMT_DOUBLE, nullptr, &writeValue); - + if (getValueResult1 == ERROR_SUCCESS && getValueResult2 == ERROR_SUCCESS) { perf.readSpeed = readValue.doubleValue * PAGE_SIZE_KB * KB_TO_MB; perf.writeSpeed = writeValue.doubleValue * PAGE_SIZE_KB * KB_TO_MB; @@ -306,13 +306,13 @@ auto getMemoryPerformance() -> MemoryPerformance { std::vector testData; testData.reserve(MEMORY_TEST_SIZE); - + const auto start = std::chrono::high_resolution_clock::now(); for (int i = 0; i < MEMORY_TEST_SIZE; ++i) { testData.push_back(i); } const auto end = std::chrono::high_resolution_clock::now(); - + perf.latency = std::chrono::duration_cast(end - start).count() / static_cast(MEMORY_TEST_SIZE); spdlog::debug("Memory performance - Read: {:.2f} MB/s, Write: {:.2f} MB/s, Bandwidth: {:.1f}%, Latency: {:.2f} ns", diff --git a/atom/sysinfo/os.hpp b/atom/sysinfo/os.hpp index e72674aa..6f3c4730 100644 --- a/atom/sysinfo/os.hpp +++ b/atom/sysinfo/os.hpp @@ -1,10 +1,10 @@ /** * @file os.hpp * @brief Operating System Information Module - * + * * This file contains definitions for retrieving comprehensive operating system * information across different platforms including Windows, Linux, and macOS. - * + * * @copyright Copyright (C) 2023-2024 Max Qian */ @@ -22,7 +22,7 @@ namespace atom::system { /** * @struct OperatingSystemInfo * @brief Comprehensive information about the operating system - * + * * Contains detailed information about the operating system including * version details, architecture, boot time, and system configuration. */ @@ -64,96 +64,96 @@ struct OperatingSystemInfo { /** * @brief Retrieves comprehensive information about the operating system - * + * * Queries the operating system for detailed information including name, * version, kernel details, architecture, and other system properties. - * + * * @return OperatingSystemInfo struct containing the operating system information */ OperatingSystemInfo getOperatingSystemInfo(); /** * @brief Checks if the operating system is running in a Windows Subsystem for Linux (WSL) environment - * + * * Detects whether the current environment is running under WSL by examining * system files and environment indicators. - * + * * @return true if the operating system is running in a WSL environment, false otherwise */ auto isWsl() -> bool; /** * @brief Retrieves the system uptime - * + * * Calculates the duration since the system was last booted. - * + * * @return The system uptime as a duration in seconds */ auto getSystemUptime() -> std::chrono::seconds; /** * @brief Retrieves the last boot time of the system - * + * * Determines when the system was last started by calculating the boot time * based on current time and uptime. - * + * * @return The last boot time as a formatted string */ auto getLastBootTime() -> std::string; /** * @brief Retrieves the system timezone - * + * * Gets the current timezone configuration of the system. - * + * * @return The system timezone as a string */ auto getSystemTimeZone() -> std::string; /** * @brief Retrieves the list of installed updates - * + * * Queries the system for a list of installed updates, patches, or packages * depending on the operating system. - * + * * @return A vector containing the names of installed updates */ auto getInstalledUpdates() -> std::vector; /** * @brief Checks for available updates - * + * * Queries the system or update repositories for available updates * that can be installed. - * + * * @return A vector containing the names of available updates */ auto checkForUpdates() -> std::vector; /** * @brief Retrieves the system language - * + * * Gets the primary language configured for the system. - * + * * @return The system language as a string */ auto getSystemLanguage() -> std::string; /** * @brief Retrieves the system encoding - * + * * Gets the character encoding used by the system. - * + * * @return The system encoding as a string */ auto getSystemEncoding() -> std::string; /** * @brief Checks if the operating system is a server edition - * + * * Determines whether the current OS installation is a server variant * or desktop/workstation variant. - * + * * @return true if the operating system is a server edition, false otherwise */ auto isServerEdition() -> bool; diff --git a/atom/sysinfo/sysinfo_printer.cpp b/atom/sysinfo/sysinfo_printer.cpp index 6214c223..5dfec90a 100644 --- a/atom/sysinfo/sysinfo_printer.cpp +++ b/atom/sysinfo/sysinfo_printer.cpp @@ -113,18 +113,18 @@ auto SystemInfoPrinter::formatBiosInfo(const BiosInfoData& info) -> std::string auto SystemInfoPrinter::formatDiskInfo(const std::vector& disks) -> std::string { std::stringstream ss; ss << createTableHeader("Disk Information"); - + for (size_t i = 0; i < disks.size(); ++i) { const auto& disk = disks[i]; ss << createTableRow("Disk " + std::to_string(i + 1) + " Model", disk.model); - ss << createTableRow("Disk " + std::to_string(i + 1) + " Type", + ss << createTableRow("Disk " + std::to_string(i + 1) + " Type", diskTypeToString(disk.fsType)); - ss << createTableRow("Disk " + std::to_string(i + 1) + " Size", + ss << createTableRow("Disk " + std::to_string(i + 1) + " Size", std::format("{:.2f} GB", disk.totalSpace / (1024.0 * 1024 * 1024))); - ss << createTableRow("Disk " + std::to_string(i + 1) + " Free Space", + ss << createTableRow("Disk " + std::to_string(i + 1) + " Free Space", std::format("{:.2f} GB", disk.freeSpace / (1024.0 * 1024 * 1024))); } - + ss << createTableFooter(); return ss.str(); } @@ -255,13 +255,13 @@ auto SystemInfoPrinter::generateSimpleReport() -> std::string { auto memInfo = getDetailedMemoryStats(); ss << "OS: " << osInfo.osName << " " << osInfo.osVersion << "\n"; - ss << "CPU: " << cpuInfo.model << " (" << cpuInfo.numPhysicalCores + ss << "CPU: " << cpuInfo.model << " (" << cpuInfo.numPhysicalCores << " cores, " << cpuInfo.numLogicalCores << " threads)\n"; - ss << "Memory: " << std::format("{:.2f} GB / {:.2f} GB ({:.1f}% used)\n", + ss << "Memory: " << std::format("{:.2f} GB / {:.2f} GB ({:.1f}% used)\n", (memInfo.totalPhysicalMemory - memInfo.availablePhysicalMemory) / (1024.0 * 1024 * 1024), memInfo.totalPhysicalMemory / (1024.0 * 1024 * 1024), memInfo.memoryLoadPercentage); - + auto batteryResult = getDetailedBatteryInfo(); if (std::holds_alternative(batteryResult)) { const auto& batteryInfo = std::get(batteryResult); @@ -394,8 +394,8 @@ bool SystemInfoPrinter::exportToHTML(const std::string& filename) {

System Information Report

-

Generated at: )" + - std::format("{:%Y-%m-%d %H:%M:%S}", std::chrono::system_clock::now()) + +

Generated at: )" + + std::format("{:%Y-%m-%d %H:%M:%S}", std::chrono::system_clock::now()) + R"(

)"; @@ -403,7 +403,7 @@ R"(

std::string currentLine; std::istringstream reportStream(report); bool inTable = false; - + while (std::getline(reportStream, currentLine)) { if (currentLine.find("===") != std::string::npos) { html += "

" + currentLine + "

\n"; @@ -421,18 +421,18 @@ R"(

if (middlePipe != std::string::npos) { std::string param = currentLine.substr(1, middlePipe - 1); std::string value = currentLine.substr(middlePipe + 1); - + // Remove trailing pipe and trim if (!value.empty() && value.back() == '|') { value.pop_back(); } - + // Trim spaces param.erase(0, param.find_first_not_of(" ")); param.erase(param.find_last_not_of(" ") + 1); value.erase(0, value.find_first_not_of(" ")); value.erase(value.find_last_not_of(" ") + 1); - + html += "" + param + "" + value + "\n"; } } @@ -444,7 +444,7 @@ R"(

html += "

" + currentLine + "

\n"; } } - + if (inTable) { html += "\n"; } @@ -469,9 +469,9 @@ bool SystemInfoPrinter::exportToJSON(const std::string& filename) { // Create a JSON structure with system information file << "{\n"; - file << " \"timestamp\": \"" << + file << " \"timestamp\": \"" << std::format("{:%Y-%m-%d %H:%M:%S}", std::chrono::system_clock::now()) << "\",\n"; - + // OS information try { auto osInfo = getOperatingSystemInfo(); @@ -492,7 +492,7 @@ bool SystemInfoPrinter::exportToJSON(const std::string& filename) { spdlog::error("Error getting OS info for JSON export: {}", e.what()); file << " \"os\": { \"error\": \"" << e.what() << "\" },\n"; } - + // CPU information try { auto cpuInfo = getCpuInfo(); @@ -510,7 +510,7 @@ bool SystemInfoPrinter::exportToJSON(const std::string& filename) { spdlog::error("Error getting CPU info for JSON export: {}", e.what()); file << " \"cpu\": { \"error\": \"" << e.what() << "\" },\n"; } - + // Memory information try { auto memInfo = getDetailedMemoryStats(); @@ -524,7 +524,7 @@ bool SystemInfoPrinter::exportToJSON(const std::string& filename) { spdlog::error("Error getting memory info for JSON export: {}", e.what()); file << " \"memory\": { \"error\": \"" << e.what() << "\" }\n"; } - + file << "}\n"; return true; } catch (const std::exception& e) { @@ -543,7 +543,7 @@ bool SystemInfoPrinter::exportToMarkdown(const std::string& filename) { } file << "# System Information Report\n\n"; - file << "Generated at: " << + file << "Generated at: " << std::format("{:%Y-%m-%d %H:%M:%S}", std::chrono::system_clock::now()) << "\n\n"; // Operating system information @@ -567,9 +567,9 @@ bool SystemInfoPrinter::exportToMarkdown(const std::string& filename) { spdlog::error("Error getting OS info for Markdown export: {}", e.what()); file << "Error retrieving operating system information: " << e.what() << "\n\n"; } - + // Add additional sections for CPU, memory, etc. - + return true; } catch (const std::exception& e) { spdlog::error("Error exporting to Markdown: {}", e.what()); @@ -577,4 +577,4 @@ bool SystemInfoPrinter::exportToMarkdown(const std::string& filename) { } } -} // namespace atom::system \ No newline at end of file +} // namespace atom::system diff --git a/atom/sysinfo/sysinfo_printer.hpp b/atom/sysinfo/sysinfo_printer.hpp index 69d2fa19..0d80fa6c 100644 --- a/atom/sysinfo/sysinfo_printer.hpp +++ b/atom/sysinfo/sysinfo_printer.hpp @@ -11,10 +11,10 @@ #ifndef ATOM_SYSINFO_PRINTER_HPP #define ATOM_SYSINFO_PRINTER_HPP - + #include #include - + #include "battery.hpp" #include "bios.hpp" #include "cpu.hpp" @@ -23,9 +23,9 @@ #include "memory.hpp" #include "os.hpp" #include "wm.hpp" - + namespace atom::system { - + /** * @class SystemInfoPrinter * @brief Formats and presents system information in human-readable formats @@ -42,21 +42,21 @@ * @return A formatted string containing battery details */ static auto formatBatteryInfo(const BatteryInfo& info) -> std::string; - + /** * @brief Format BIOS information as a string * @param info The BIOS information to format * @return A formatted string containing BIOS details */ static auto formatBiosInfo(const BiosInfoData& info) -> std::string; - + /** * @brief Format CPU information as a string * @param info The CPU information to format * @return A formatted string containing CPU details */ static auto formatCpuInfo(const CpuInfo& info) -> std::string; - + /** * @brief Format disk information as a string * @param info Vector of disk information objects to format @@ -64,114 +64,114 @@ */ static auto formatDiskInfo(const std::vector& info) -> std::string; - + /** * @brief Format GPU information as a string * @return A formatted string containing GPU details */ static auto formatGpuInfo() -> std::string; - + /** * @brief Format locale information as a string * @param info The locale information to format * @return A formatted string containing locale settings */ static auto formatLocaleInfo(const LocaleInfo& info) -> std::string; - + /** * @brief Format memory information as a string * @param info The memory information to format * @return A formatted string containing memory details */ static auto formatMemoryInfo(const MemoryInfo& info) -> std::string; - + /** * @brief Format operating system information as a string * @param info The OS information to format * @return A formatted string containing OS details */ static auto formatOsInfo(const OperatingSystemInfo& info) -> std::string; - + /** * @brief Format comprehensive system information as a string * @param info The system information structure to format * @return A formatted string containing system details */ static auto formatSystemInfo(const SystemInfo& info) -> std::string; - + /** * @brief Generate a comprehensive report of all system components - * + * * Creates a detailed report including information about all hardware and * software components of the system. - * + * * @return A string containing the full system report */ static auto generateFullReport() -> std::string; - + /** * @brief Generate a simplified overview of key system information - * + * * Creates a concise report with the most important system details * suitable for quick reference. - * + * * @return A string containing the simplified system report */ static auto generateSimpleReport() -> std::string; - + /** * @brief Generate a report focused on system performance metrics - * + * * Creates a report with emphasis on performance-related information * like CPU speed, memory usage, disk speeds, etc. - * + * * @return A string containing the performance-focused report */ static auto generatePerformanceReport() -> std::string; - + /** * @brief Generate a report focused on system security features - * + * * Creates a report highlighting security-related information such as * OS security features, firmware versions, and potential vulnerabilities. - * + * * @return A string containing the security-focused report */ static auto generateSecurityReport() -> std::string; - + /** * @brief Export system information to HTML format - * + * * Generates a complete system information report and saves it as an * HTML file at the specified location. - * + * * @param filename The path where the HTML file will be saved * @return true if export was successful, false otherwise */ static bool exportToHTML(const std::string& filename); - + /** * @brief Export system information to JSON format - * + * * Generates a complete system information report and saves it as a * structured JSON file at the specified location. - * + * * @param filename The path where the JSON file will be saved * @return true if export was successful, false otherwise */ static bool exportToJSON(const std::string& filename); - + /** * @brief Export system information to Markdown format - * + * * Generates a complete system information report and saves it as a * Markdown file at the specified location. - * + * * @param filename The path where the Markdown file will be saved * @return true if export was successful, false otherwise */ static bool exportToMarkdown(const std::string& filename); - + private: /** * @brief Helper method to create a formatted table row @@ -181,21 +181,21 @@ */ static auto createTableRow(const std::string& label, const std::string& value) -> std::string; - + /** * @brief Helper method to create a formatted table header * @param title The title of the table * @return A formatted string representing a table header */ static auto createTableHeader(const std::string& title) -> std::string; - + /** * @brief Helper method to create a formatted table footer * @return A formatted string representing a table footer */ static auto createTableFooter() -> std::string; }; - + } // namespace atom::system - - #endif // ATOM_SYSINFO_PRINTER_HPP \ No newline at end of file + + #endif // ATOM_SYSINFO_PRINTER_HPP diff --git a/atom/sysinfo/virtual.hpp b/atom/sysinfo/virtual.hpp index e7b6cceb..fcf4514d 100644 --- a/atom/sysinfo/virtual.hpp +++ b/atom/sysinfo/virtual.hpp @@ -185,4 +185,4 @@ auto getContainerType() -> std::string; } // namespace atom::system -#endif // ATOM_SYSINFO_VIRTUAL_HPP \ No newline at end of file +#endif // ATOM_SYSINFO_VIRTUAL_HPP diff --git a/atom/sysinfo/wifi/wifi.cpp b/atom/sysinfo/wifi/wifi.cpp index 39e8ddd6..b09e9794 100644 --- a/atom/sysinfo/wifi/wifi.cpp +++ b/atom/sysinfo/wifi/wifi.cpp @@ -102,7 +102,7 @@ auto getIPAddresses(int addressFamily) -> std::vector { if (ua->Address.lpSockaddr->sa_family == addressFamily) { char ipStr[INET6_ADDRSTRLEN] = {0}; void* addr = nullptr; - + if (addressFamily == AF_INET) { struct sockaddr_in* ipv4 = reinterpret_cast(ua->Address.lpSockaddr); addr = &(ipv4->sin_addr); @@ -134,7 +134,7 @@ auto getIPAddresses(int addressFamily) -> std::vector { if (ifa->ifa_addr && ifa->ifa_addr->sa_family == addressFamily) { char ipStr[INET6_ADDRSTRLEN] = {0}; void* addr = nullptr; - + if (addressFamily == AF_INET) { struct sockaddr_in* ipv4 = reinterpret_cast(ifa->ifa_addr); addr = &(ipv4->sin_addr); @@ -142,7 +142,7 @@ auto getIPAddresses(int addressFamily) -> std::vector { struct sockaddr_in6* ipv6 = reinterpret_cast(ifa->ifa_addr); addr = &(ipv6->sin6_addr); } - + inet_ntop(addressFamily, addr, ipStr, sizeof(ipStr)); addresses.emplace_back(ipStr); LOG_F(INFO, "Found IP address: {}", ipStr); diff --git a/atom/sysinfo/xmake.lua b/atom/sysinfo/xmake.lua index 3f67ae62..6e70f20d 100644 --- a/atom/sysinfo/xmake.lua +++ b/atom/sysinfo/xmake.lua @@ -36,25 +36,25 @@ local header_files = { -- Object Library target("atom-sysinfo-object") set_kind("object") - + -- Add files add_files(table.unpack(source_files)) add_headerfiles(table.unpack(header_files)) - + -- Add dependencies add_packages("loguru") - + -- Add include directories add_includedirs(".", {public = true}) add_includedirs("..", {public = true}) - + -- Platform-specific settings if is_plat("linux") then add_syslinks("pthread") elseif is_plat("windows") then add_syslinks("pdh", "wlanapi") end - + -- Set C++ standard set_languages("c++20") target_end() @@ -63,25 +63,25 @@ target_end() target("atom-sysinfo") -- Set library type based on parent project option set_kind(has_config("shared_libs") and "shared" or "static") - + -- Add dependencies add_deps("atom-sysinfo-object") add_packages("loguru") - + -- Platform-specific settings if is_plat("linux") then add_syslinks("pthread") elseif is_plat("windows") then add_syslinks("pdh", "wlanapi") end - + -- Set output directories set_targetdir("$(buildir)/lib") set_objectdir("$(buildir)/obj") - + -- Set version with build timestamp set_version("1.0.0", {build = "%Y%m%d%H%M"}) - + -- Install configuration on_install(function (target) os.cp(target:targetfile(), path.join(target:installdir(), "lib")) diff --git a/atom/system/CMakeLists.txt b/atom/system/CMakeLists.txt index f683dfe9..74aab780 100644 --- a/atom/system/CMakeLists.txt +++ b/atom/system/CMakeLists.txt @@ -94,4 +94,4 @@ install(TARGETS ${PROJECT_NAME} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} PUBLIC_HEADER DESTINATION include/${PROJECT_NAME} -) \ No newline at end of file +) diff --git a/atom/system/clipboard.ipp b/atom/system/clipboard.ipp index bcc2f069..10b29c86 100644 --- a/atom/system/clipboard.ipp +++ b/atom/system/clipboard.ipp @@ -14,7 +14,7 @@ public: // ============================================================================ // Core Operations // ============================================================================ - + virtual bool open() = 0; virtual void close() noexcept = 0; virtual bool clear() = 0; @@ -22,14 +22,14 @@ public: // ============================================================================ // Text Operations // ============================================================================ - + virtual bool setText(std::string_view text) = 0; virtual std::optional getText() = 0; // ============================================================================ // Binary Data Operations // ============================================================================ - + virtual bool setData(ClipboardFormat format, std::span data) = 0; virtual std::optional> getData(ClipboardFormat format) = 0; virtual bool containsFormat(ClipboardFormat format) = 0; @@ -37,7 +37,7 @@ public: // ============================================================================ // Image Operations // ============================================================================ - + #ifdef CLIPBOARD_SUPPORT_OPENCV virtual bool setImage(const cv::Mat& image) = 0; virtual std::optional getImageAsMat() = 0; @@ -51,7 +51,7 @@ public: // ============================================================================ // Query Operations // ============================================================================ - + virtual bool hasText() = 0; virtual bool hasImage() = 0; virtual std::vector getAvailableFormats() = 0; @@ -60,16 +60,16 @@ public: // ============================================================================ // Change Monitoring // ============================================================================ - + virtual bool hasChanged() const { return false; } virtual void updateChangeCount() {} // ============================================================================ // Static Factory Methods // ============================================================================ - + static std::unique_ptr create(); static ClipboardFormat registerFormat(std::string_view formatName); }; -} // namespace clip \ No newline at end of file +} // namespace clip diff --git a/atom/system/clipboard_error.hpp b/atom/system/clipboard_error.hpp index ac03c9a9..6eaac335 100644 --- a/atom/system/clipboard_error.hpp +++ b/atom/system/clipboard_error.hpp @@ -224,4 +224,4 @@ template return ScopeGuard>(std::forward(f)); } -} // namespace clip \ No newline at end of file +} // namespace clip diff --git a/atom/system/clipboard_macos.cpp b/atom/system/clipboard_macos.cpp index c263fabb..97144fe1 100644 --- a/atom/system/clipboard_macos.cpp +++ b/atom/system/clipboard_macos.cpp @@ -19,20 +19,20 @@ namespace { class CFStringWrapper { public: explicit CFStringWrapper(CFStringRef str) : m_string(str) {} - + ~CFStringWrapper() { if (m_string) { CFRelease(m_string); } } - + CFStringWrapper(const CFStringWrapper&) = delete; CFStringWrapper& operator=(const CFStringWrapper&) = delete; - + CFStringWrapper(CFStringWrapper&& other) noexcept : m_string(other.m_string) { other.m_string = nullptr; } - + CFStringWrapper& operator=(CFStringWrapper&& other) noexcept { if (this != &other) { if (m_string) { @@ -43,14 +43,14 @@ namespace { } return *this; } - + CFStringRef get() const noexcept { return m_string; } operator bool() const noexcept { return m_string != nullptr; } - + private: CFStringRef m_string; }; - + // Helper function to create CFString from std::string CFStringWrapper createCFString(std::string_view text) { CFStringRef str = CFStringCreateWithBytes( @@ -62,19 +62,19 @@ namespace { ); return CFStringWrapper(str); } - + // Helper function to convert CFString to std::string std::string cfStringToString(CFStringRef cfStr) { if (!cfStr) { return {}; } - + CFIndex length = CFStringGetLength(cfStr); CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8); - + std::string result(maxSize, '\0'); CFIndex actualSize = 0; - + Boolean success = CFStringGetBytes( cfStr, CFRangeMake(0, length), @@ -85,15 +85,15 @@ namespace { maxSize, &actualSize ); - + if (success) { result.resize(actualSize); return result; } - + return {}; } - + // Convert error to ClipboardErrorCode ClipboardErrorCode osStatusToErrorCode(OSStatus status) { switch (status) { @@ -140,7 +140,7 @@ class MacOSClipboard : public Clipboard::Impl { if (!m_pasteboard) { return false; } - + [m_pasteboard clearContents]; m_changeCount = [m_pasteboard changeCount]; return true; @@ -152,27 +152,27 @@ class MacOSClipboard : public Clipboard::Impl { if (!m_pasteboard) { return false; } - + try { - NSString* nsString = [[NSString alloc] - initWithBytes:text.data() - length:text.size() + NSString* nsString = [[NSString alloc] + initWithBytes:text.data() + length:text.size() encoding:NSUTF8StringEncoding]; - + if (!nsString) { throw ClipboardFormatException("Failed to create NSString from text"); } - + auto guard = make_scope_guard([nsString] { [nsString release]; }); - + [m_pasteboard clearContents]; BOOL success = [m_pasteboard setString:nsString forType:NSPasteboardTypeString]; - + if (success) { m_changeCount = [m_pasteboard changeCount]; return true; } - + return false; } catch (const std::exception&) { return false; @@ -185,18 +185,18 @@ class MacOSClipboard : public Clipboard::Impl { if (!m_pasteboard) { return std::nullopt; } - + try { NSString* string = [m_pasteboard stringForType:NSPasteboardTypeString]; if (!string) { return std::nullopt; } - + const char* cString = [string UTF8String]; if (!cString) { return std::nullopt; } - + return std::string(cString); } catch (const std::exception&) { return std::nullopt; @@ -209,27 +209,27 @@ class MacOSClipboard : public Clipboard::Impl { if (!m_pasteboard) { return false; } - + try { // Convert format to pasteboard type NSString* pasteboardType = formatToPasteboardType(format); if (!pasteboardType) { return false; } - + NSData* nsData = [NSData dataWithBytes:data.data() length:data.size()]; if (!nsData) { return false; } - + [m_pasteboard clearContents]; BOOL success = [m_pasteboard setData:nsData forType:pasteboardType]; - + if (success) { m_changeCount = [m_pasteboard changeCount]; return true; } - + return false; } catch (const std::exception&) { return false; @@ -242,21 +242,21 @@ class MacOSClipboard : public Clipboard::Impl { if (!m_pasteboard) { return std::nullopt; } - + try { NSString* pasteboardType = formatToPasteboardType(format); if (!pasteboardType) { return std::nullopt; } - + NSData* data = [m_pasteboard dataForType:pasteboardType]; if (!data) { return std::nullopt; } - + std::vector result(data.length); std::memcpy(result.data(), data.bytes, data.length); - + return result; } catch (const std::exception&) { return std::nullopt; @@ -269,12 +269,12 @@ class MacOSClipboard : public Clipboard::Impl { if (!m_pasteboard) { return false; } - + NSString* pasteboardType = formatToPasteboardType(format); if (!pasteboardType) { return false; } - + NSArray* types = [m_pasteboard types]; return [types containsObject:pasteboardType]; } @@ -286,7 +286,7 @@ class MacOSClipboard : public Clipboard::Impl { if (!m_pasteboard || image.empty()) { return false; } - + try { // Convert cv::Mat to NSImage cv::Mat rgbImage; @@ -297,7 +297,7 @@ class MacOSClipboard : public Clipboard::Impl { } else { rgbImage = image.clone(); } - + NSBitmapImageRep* imageRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil pixelsWide:rgbImage.cols @@ -309,28 +309,28 @@ class MacOSClipboard : public Clipboard::Impl { colorSpaceName:NSCalibratedRGBColorSpace bytesPerRow:rgbImage.step bitsPerPixel:rgbImage.channels() * 8]; - + if (!imageRep) { return false; } - + auto guard = make_scope_guard([imageRep] { [imageRep release]; }); - + std::memcpy([imageRep bitmapData], rgbImage.data, rgbImage.total() * rgbImage.elemSize()); - + NSImage* nsImage = [[NSImage alloc] init]; [nsImage addRepresentation:imageRep]; - + auto imageGuard = make_scope_guard([nsImage] { [nsImage release]; }); - + [m_pasteboard clearContents]; BOOL success = [m_pasteboard writeObjects:@[nsImage]]; - + if (success) { m_changeCount = [m_pasteboard changeCount]; return true; } - + return false; } catch (const std::exception&) { return false; @@ -343,18 +343,18 @@ class MacOSClipboard : public Clipboard::Impl { if (!m_pasteboard) { return std::nullopt; } - + try { NSArray* images = [m_pasteboard readObjectsForClasses:@[[NSImage class]] options:nil]; if (!images || images.count == 0) { return std::nullopt; } - + NSImage* image = [images firstObject]; if (!image) { return std::nullopt; } - + NSBitmapImageRep* imageRep = nil; for (NSImageRep* rep in [image representations]) { if ([rep isKindOfClass:[NSBitmapImageRep class]]) { @@ -362,31 +362,31 @@ class MacOSClipboard : public Clipboard::Impl { break; } } - + if (!imageRep) { // Convert to bitmap representation NSSize imageSize = [image size]; [image lockFocus]; imageRep = [[NSBitmapImageRep alloc] initWithFocusedViewRect:NSMakeRect(0, 0, imageSize.width, imageSize.height)]; [image unlockFocus]; - + if (!imageRep) { return std::nullopt; } } - + auto guard = make_scope_guard([imageRep] { [imageRep release]; }); - + int width = (int)[imageRep pixelsWide]; int height = (int)[imageRep pixelsHigh]; int channels = (int)[imageRep samplesPerPixel]; - + cv::Mat result(height, width, CV_8UC(channels)); - + unsigned char* imageData = [imageRep bitmapData]; if (imageData) { std::memcpy(result.data, imageData, result.total() * result.elemSize()); - + // Convert from RGB to BGR for OpenCV if (channels == 3) { cv::cvtColor(result, result, cv::COLOR_RGB2BGR); @@ -394,7 +394,7 @@ class MacOSClipboard : public Clipboard::Impl { cv::cvtColor(result, result, cv::COLOR_RGBA2BGRA); } } - + return result; } catch (const std::exception&) { return std::nullopt; @@ -408,7 +408,7 @@ class MacOSClipboard : public Clipboard::Impl { // Convert CImg to cv::Mat and use OpenCV implementation #ifdef CLIPBOARD_SUPPORT_OPENCV cv::Mat mat(image.height(), image.width(), CV_8UC(image.spectrum())); - + if (image.spectrum() == 1) { // Grayscale std::memcpy(mat.data, image.data(), image.size()); @@ -422,7 +422,7 @@ class MacOSClipboard : public Clipboard::Impl { } } } - + return setImage(mat); #else return false; @@ -435,9 +435,9 @@ class MacOSClipboard : public Clipboard::Impl { if (!mat) { return std::nullopt; } - + cimg_library::CImg result(mat->cols, mat->rows, 1, mat->channels()); - + if (mat->channels() == 1) { std::memcpy(result.data(), mat->data, mat->total()); } else if (mat->channels() == 3) { @@ -450,7 +450,7 @@ class MacOSClipboard : public Clipboard::Impl { } } } - + return result; #else return std::nullopt; @@ -463,7 +463,7 @@ class MacOSClipboard : public Clipboard::Impl { if (!m_pasteboard) { return false; } - + NSArray* types = [m_pasteboard types]; return [types containsObject:NSPasteboardTypeString]; } @@ -474,7 +474,7 @@ class MacOSClipboard : public Clipboard::Impl { if (!m_pasteboard) { return false; } - + NSArray* types = [m_pasteboard types]; return [types containsObject:NSPasteboardTypeTIFF] || [types containsObject:NSPasteboardTypePNG]; @@ -484,11 +484,11 @@ class MacOSClipboard : public Clipboard::Impl { std::vector getAvailableFormats() override { @autoreleasepool { std::vector formats; - + if (!m_pasteboard) { return formats; } - + NSArray* types = [m_pasteboard types]; for (NSString* type in types) { unsigned int format = pasteboardTypeToFormat(type); @@ -496,7 +496,7 @@ class MacOSClipboard : public Clipboard::Impl { formats.push_back(format); } } - + return formats; } } @@ -515,7 +515,7 @@ class MacOSClipboard : public Clipboard::Impl { if (!m_pasteboard) { return false; } - + NSInteger currentChangeCount = [m_pasteboard changeCount]; return currentChangeCount != m_changeCount; } @@ -532,7 +532,7 @@ class MacOSClipboard : public Clipboard::Impl { private: NSPasteboard* m_pasteboard = nullptr; NSInteger m_changeCount = 0; - + // Format conversion mappings std::unordered_map m_formatToType = { {1, NSPasteboardTypeString}, @@ -541,14 +541,14 @@ class MacOSClipboard : public Clipboard::Impl { {4, NSPasteboardTypePNG}, {5, NSPasteboardTypeRTF} }; - + std::unordered_map m_typeToFormat; - + NSString* formatToPasteboardType(unsigned int format) { auto it = m_formatToType.find(format); return (it != m_formatToType.end()) ? it->second : nil; } - + unsigned int pasteboardTypeToFormat(NSString* type) { if (m_typeToFormat.empty()) { // Initialize reverse mapping @@ -556,7 +556,7 @@ class MacOSClipboard : public Clipboard::Impl { m_typeToFormat[pair.second] = pair.first; } } - + auto it = m_typeToFormat.find(type); return (it != m_typeToFormat.end()) ? it->second : 0; } @@ -570,17 +570,17 @@ std::unique_ptr Clipboard::Impl::create() { // Static format registration method unsigned int Clipboard::Impl::registerFormat(std::string_view formatName) { @autoreleasepool { - NSString* nsFormatName = [[NSString alloc] - initWithBytes:formatName.data() - length:formatName.size() + NSString* nsFormatName = [[NSString alloc] + initWithBytes:formatName.data() + length:formatName.size() encoding:NSUTF8StringEncoding]; - + if (!nsFormatName) { return 0; } - + auto guard = make_scope_guard([nsFormatName] { [nsFormatName release]; }); - + // In macOS, we use the string directly as the pasteboard type // Return a hash of the format name as the format ID std::hash hasher; diff --git a/atom/system/clipboard_windows.cpp b/atom/system/clipboard_windows.cpp index ea97c7c2..9ba92d4b 100644 --- a/atom/system/clipboard_windows.cpp +++ b/atom/system/clipboard_windows.cpp @@ -661,9 +661,9 @@ class WindowsClipboard : public Clipboard::Impl { // ============================================================================ // Change Monitoring Implementation // ============================================================================ - + bool hasChanged() const override { - // Windows doesn't provide built-in change detection, + // Windows doesn't provide built-in change detection, // so we'll use a simple sequence number approach DWORD currentSequence = GetClipboardSequenceNumber(); if (currentSequence != m_lastSequenceNumber) { @@ -672,7 +672,7 @@ class WindowsClipboard : public Clipboard::Impl { } return false; } - + void updateChangeCount() override { m_lastSequenceNumber = GetClipboardSequenceNumber(); }std::vector getAvailableFormats() override { @@ -766,4 +766,4 @@ ClipboardFormat Clipboard::Impl::registerFormat(std::string_view formatName) { } // namespace clip -#endif // _WIN32 \ No newline at end of file +#endif // _WIN32 diff --git a/atom/system/crontab.cpp b/atom/system/crontab.cpp index a90313df..d3e5ab65 100644 --- a/atom/system/crontab.cpp +++ b/atom/system/crontab.cpp @@ -853,4 +853,4 @@ auto CronManager::getJobsByPriority() -> std::vector { }); return sortedJobs; -} \ No newline at end of file +} diff --git a/atom/system/crontab.hpp b/atom/system/crontab.hpp index 0a3d7a77..eb8db1ed 100644 --- a/atom/system/crontab.hpp +++ b/atom/system/crontab.hpp @@ -366,4 +366,4 @@ class CronManager { auto handleJobFailure(const std::string& id) -> bool; }; -#endif // CRONJOB_H \ No newline at end of file +#endif // CRONJOB_H diff --git a/atom/system/gpio.hpp b/atom/system/gpio.hpp index c6f60f05..e33896de 100644 --- a/atom/system/gpio.hpp +++ b/atom/system/gpio.hpp @@ -362,4 +362,4 @@ std::string edgeToString(GPIO::Edge edge); } // namespace atom::system -#endif // ATOM_SYSTEM_GPIO_HPP \ No newline at end of file +#endif // ATOM_SYSTEM_GPIO_HPP diff --git a/atom/system/lregistry.cpp b/atom/system/lregistry.cpp index b1a0ae54..76103a51 100644 --- a/atom/system/lregistry.cpp +++ b/atom/system/lregistry.cpp @@ -1435,4 +1435,4 @@ bool Registry::RegistryImpl::matchesPattern(const std::string& text, } } -} // namespace atom::system \ No newline at end of file +} // namespace atom::system diff --git a/atom/system/lregistry.hpp b/atom/system/lregistry.hpp index f3976e73..1d669715 100644 --- a/atom/system/lregistry.hpp +++ b/atom/system/lregistry.hpp @@ -322,4 +322,4 @@ class Registry { } // namespace atom::system -#endif \ No newline at end of file +#endif diff --git a/atom/system/network_manager.hpp b/atom/system/network_manager.hpp index 76f1756f..9dbdcc82 100644 --- a/atom/system/network_manager.hpp +++ b/atom/system/network_manager.hpp @@ -186,4 +186,4 @@ auto getNetworkConnections(int pid) -> std::vector; } // namespace atom::system -#endif // ATOM_SYSTEM_NETWORK_MANAGER_HPP \ No newline at end of file +#endif // ATOM_SYSTEM_NETWORK_MANAGER_HPP diff --git a/atom/system/nodebugger.cpp b/atom/system/nodebugger.cpp index f4495b0d..92baa86e 100644 --- a/atom/system/nodebugger.cpp +++ b/atom/system/nodebugger.cpp @@ -362,4 +362,4 @@ void stopAntiDebugMonitoring() { g_monitoringThread.join(); } } -} // namespace atom::system \ No newline at end of file +} // namespace atom::system diff --git a/atom/system/nodebugger.hpp b/atom/system/nodebugger.hpp index 681b616c..bf4cfa1b 100644 --- a/atom/system/nodebugger.hpp +++ b/atom/system/nodebugger.hpp @@ -56,4 +56,4 @@ void enableSelfModifyingCode(void* codeAddress, size_t codeSize); } // namespace atom::system -#endif // ATOM_SYSTEM_NODEBUGGER_HPP \ No newline at end of file +#endif // ATOM_SYSTEM_NODEBUGGER_HPP diff --git a/atom/system/pidwatcher.cpp b/atom/system/pidwatcher.cpp index 437b0ef1..d279a556 100644 --- a/atom/system/pidwatcher.cpp +++ b/atom/system/pidwatcher.cpp @@ -2462,4 +2462,4 @@ void PidWatcher::watchdogThread() { spdlog::info("Watchdog thread exited"); } -} // namespace atom::system \ No newline at end of file +} // namespace atom::system diff --git a/atom/system/pidwatcher.hpp b/atom/system/pidwatcher.hpp index 6e862102..4d3e074f 100644 --- a/atom/system/pidwatcher.hpp +++ b/atom/system/pidwatcher.hpp @@ -467,4 +467,4 @@ class PidWatcher { } // namespace atom::system -#endif \ No newline at end of file +#endif diff --git a/atom/system/shortcut/CMakeLists.txt b/atom/system/shortcut/CMakeLists.txt index 438d71ca..52f591ab 100644 --- a/atom/system/shortcut/CMakeLists.txt +++ b/atom/system/shortcut/CMakeLists.txt @@ -26,7 +26,7 @@ add_library(shortcut_detector SHARED # Set include directories target_include_directories(shortcut_detector - PUBLIC + PUBLIC $ $ PRIVATE @@ -50,7 +50,7 @@ add_library(shortcut_detector_static STATIC ) target_include_directories(shortcut_detector_static - PUBLIC + PUBLIC $ $ PRIVATE @@ -101,7 +101,7 @@ install( ) install( - FILES + FILES include/detector.h include/shortcut.h include/factory.h @@ -113,4 +113,4 @@ install( EXPORT shortcut_detector-config NAMESPACE shortcut_detector:: DESTINATION lib/cmake/shortcut_detector -) \ No newline at end of file +) diff --git a/atom/system/shortcut/detector.hpp b/atom/system/shortcut/detector.hpp index caede697..b4e9d37f 100644 --- a/atom/system/shortcut/detector.hpp +++ b/atom/system/shortcut/detector.hpp @@ -60,4 +60,4 @@ class ShortcutDetector { std::unique_ptr pImpl; }; -} // namespace shortcut_detector \ No newline at end of file +} // namespace shortcut_detector diff --git a/atom/system/shortcut/detector_impl.cpp b/atom/system/shortcut/detector_impl.cpp index 6f2045ed..3d00c897 100644 --- a/atom/system/shortcut/detector_impl.cpp +++ b/atom/system/shortcut/detector_impl.cpp @@ -44,13 +44,13 @@ ShortcutCheckResult ShortcutDetectorImpl::isShortcutCaptured( if (!canRegister) { const auto capturingApp = findCapturingApplication(shortcut); if (capturingApp.empty()) { - spdlog::debug("Shortcut {} is captured by unknown system component", + spdlog::debug("Shortcut {} is captured by unknown system component", shortcut.toString()); return {ShortcutStatus::CapturedBySystem, "Unknown System Component", "The shortcut is captured by the system"}; } else { - spdlog::debug("Shortcut {} is captured by application: {}", + spdlog::debug("Shortcut {} is captured by application: {}", shortcut.toString(), capturingApp); return {ShortcutStatus::CapturedByApp, capturingApp, "The shortcut is registered by another application"}; @@ -59,7 +59,7 @@ ShortcutCheckResult ShortcutDetectorImpl::isShortcutCaptured( if (hasInterceptingKeyboardHook(shortcut)) { const auto hookOwner = findKeyboardHookOwner(); - spdlog::debug("Shortcut {} may be intercepted by keyboard hook owned by: {}", + spdlog::debug("Shortcut {} may be intercepted by keyboard hook owned by: {}", shortcut.toString(), hookOwner); return {ShortcutStatus::CapturedByApp, hookOwner, "A keyboard hook may intercept this shortcut"}; @@ -138,4 +138,4 @@ std::string ShortcutDetectorImpl::findKeyboardHookOwner() { return processes[0]; } -} // namespace shortcut_detector \ No newline at end of file +} // namespace shortcut_detector diff --git a/atom/system/shortcut/detector_impl.h b/atom/system/shortcut/detector_impl.h index 9433bb97..fe529541 100644 --- a/atom/system/shortcut/detector_impl.h +++ b/atom/system/shortcut/detector_impl.h @@ -11,7 +11,7 @@ namespace shortcut_detector { /** * @brief Implementation class for ShortcutDetector using PIMPL idiom - * + * * This class provides the actual implementation for keyboard shortcut detection * on Windows systems. It checks for system-reserved shortcuts, attempts hotkey * registration, and detects keyboard hooks. @@ -23,23 +23,23 @@ class ShortcutDetectorImpl { /** * @brief Check if a keyboard shortcut is captured by system or applications - * + * * @param shortcut The shortcut to check * @return ShortcutCheckResult Result containing status and details */ ShortcutCheckResult isShortcutCaptured(const Shortcut& shortcut); - + /** * @brief Check if any keyboard hook is currently installed - * + * * @return true If keyboard hooks are detected * @return false If no keyboard hooks are detected */ bool hasKeyboardHookInstalled(); - + /** * @brief Get list of processes that have keyboard hooks installed - * + * * @return std::vector Process names with keyboard hooks */ std::vector getProcessesWithKeyboardHooks(); @@ -58,4 +58,4 @@ class ShortcutDetectorImpl { systemReservedShortcuts; }; -} // namespace shortcut_detector \ No newline at end of file +} // namespace shortcut_detector diff --git a/atom/system/shortcut/factory.cpp b/atom/system/shortcut/factory.cpp index 1170f60e..c25212a8 100644 --- a/atom/system/shortcut/factory.cpp +++ b/atom/system/shortcut/factory.cpp @@ -87,4 +87,4 @@ Shortcut ShortcutFactory::fromString(const std::string& description) { return Shortcut(vkCode, ctrl, alt, shift, win); } -} // namespace shortcut_detector \ No newline at end of file +} // namespace shortcut_detector diff --git a/atom/system/shortcut/factory.h b/atom/system/shortcut/factory.h index 1df53a70..23038853 100644 --- a/atom/system/shortcut/factory.h +++ b/atom/system/shortcut/factory.h @@ -61,4 +61,4 @@ class ShortcutFactory { } // VK_F4 = 0x73 }; -} // namespace shortcut_detector \ No newline at end of file +} // namespace shortcut_detector diff --git a/atom/system/shortcut/shortcut.cpp b/atom/system/shortcut/shortcut.cpp index 041c4a95..e1a6d891 100644 --- a/atom/system/shortcut/shortcut.cpp +++ b/atom/system/shortcut/shortcut.cpp @@ -97,4 +97,4 @@ bool Shortcut::operator==(const Shortcut& other) const { shift == other.shift && win == other.win; } -} // namespace shortcut_detector \ No newline at end of file +} // namespace shortcut_detector diff --git a/atom/system/shortcut/shortcut.h b/atom/system/shortcut/shortcut.h index 074bb196..e9a5c124 100644 --- a/atom/system/shortcut/shortcut.h +++ b/atom/system/shortcut/shortcut.h @@ -60,4 +60,4 @@ struct hash { return s.hash(); } }; -} // namespace std \ No newline at end of file +} // namespace std diff --git a/atom/system/shortcut/status.h b/atom/system/shortcut/status.h index c233831d..6bf9c9df 100644 --- a/atom/system/shortcut/status.h +++ b/atom/system/shortcut/status.h @@ -24,4 +24,4 @@ struct ShortcutCheckResult { std::string details; // Additional details }; -} // namespace shortcut_detector \ No newline at end of file +} // namespace shortcut_detector diff --git a/atom/system/shortcut/test_shortcut_detector.cpp b/atom/system/shortcut/test_shortcut_detector.cpp index 94c07ba3..e9dec597 100644 --- a/atom/system/shortcut/test_shortcut_detector.cpp +++ b/atom/system/shortcut/test_shortcut_detector.cpp @@ -14,7 +14,7 @@ int main() { try { // Create detector instance shortcut_detector::ShortcutDetector detector; - + // Test various shortcuts std::vector> testShortcuts = { {"Ctrl+C", shortcut_detector::ShortcutFactory::create('C', true, false, false, false)}, @@ -28,12 +28,12 @@ int main() { for (const auto& [name, shortcut] : testShortcuts) { spdlog::info("Testing shortcut: {} ({})", name, shortcut.toString()); - + const auto result = detector.isShortcutCaptured(shortcut); - + std::cout << "Shortcut: " << name << " (" << shortcut.toString() << ")\n"; std::cout << " Status: "; - + switch (result.status) { case shortcut_detector::ShortcutStatus::Available: std::cout << "Available"; @@ -48,7 +48,7 @@ int main() { std::cout << "Reserved by Windows"; break; } - + std::cout << "\n Details: " << result.details << "\n\n"; } diff --git a/atom/system/shortcut/win32_utils.cpp b/atom/system/shortcut/win32_utils.cpp index e9523220..9e4a4513 100644 --- a/atom/system/shortcut/win32_utils.cpp +++ b/atom/system/shortcut/win32_utils.cpp @@ -19,9 +19,9 @@ namespace win32_utils { * @brief Known keyboard hook DLL modules commonly used by applications */ static const std::unordered_set knownHookDlls = { - "HOOK.DLL", "KBDHOOK.DLL", "KEYHOOK.DLL", - "INPUTHOOK.DLL", "WINHOOK.DLL", "LLKEYBOARD.DLL", - "KEYMAGIC.DLL", "HOOKSPY.DLL", "KEYBOARDHOOK.DLL", + "HOOK.DLL", "KBDHOOK.DLL", "KEYHOOK.DLL", + "INPUTHOOK.DLL", "WINHOOK.DLL", "LLKEYBOARD.DLL", + "KEYMAGIC.DLL", "HOOKSPY.DLL", "KEYBOARDHOOK.DLL", "INPUTMANAGERHOOK.DLL", "UIHOOK.DLL"}; std::vector getProcessesWithKeyboardHooks() { @@ -125,4 +125,4 @@ bool isHookingModule(const std::string& moduleName) { } } // namespace win32_utils -} // namespace shortcut_detector \ No newline at end of file +} // namespace shortcut_detector diff --git a/atom/system/shortcut/win32_utils.h b/atom/system/shortcut/win32_utils.h index 7c268df1..543cf4ec 100644 --- a/atom/system/shortcut/win32_utils.h +++ b/atom/system/shortcut/win32_utils.h @@ -39,4 +39,4 @@ std::string getProcessName(DWORD processId); bool isHookingModule(const std::string& moduleName); } // namespace win32_utils -} // namespace shortcut_detector \ No newline at end of file +} // namespace shortcut_detector diff --git a/atom/system/software.hpp b/atom/system/software.hpp index d295b526..2c45df3f 100644 --- a/atom/system/software.hpp +++ b/atom/system/software.hpp @@ -94,4 +94,4 @@ auto checkSoftwareUpdates(const std::string& software_name, } // namespace atom::system -#endif \ No newline at end of file +#endif diff --git a/atom/system/stat.cpp b/atom/system/stat.cpp index 2302bba2..39074a5d 100644 --- a/atom/system/stat.cpp +++ b/atom/system/stat.cpp @@ -799,4 +799,4 @@ std::string Stat::formatTime(std::time_t time, const std::string& format) { return oss.str(); } -} // namespace atom::system \ No newline at end of file +} // namespace atom::system diff --git a/atom/system/stat.hpp b/atom/system/stat.hpp index fd6efa1f..c29f2c86 100644 --- a/atom/system/stat.hpp +++ b/atom/system/stat.hpp @@ -307,4 +307,4 @@ class Stat { } // namespace atom::system -#endif // ATOM_SYSTEM_STAT_HPP \ No newline at end of file +#endif // ATOM_SYSTEM_STAT_HPP diff --git a/atom/system/storage.hpp b/atom/system/storage.hpp index 7d7a6f19..4c81e720 100644 --- a/atom/system/storage.hpp +++ b/atom/system/storage.hpp @@ -197,4 +197,4 @@ void monitorUdisk(StorageMonitor& monitor); } // namespace atom::system -#endif \ No newline at end of file +#endif diff --git a/atom/system/user.cpp b/atom/system/user.cpp index 238d33bc..07bc0b3f 100644 --- a/atom/system/user.cpp +++ b/atom/system/user.cpp @@ -92,7 +92,7 @@ auto getUserGroups() -> std::vector { spdlog::error("Failed to open process token for group enumeration"); return groups; } - + DWORD bufferSize = 0; GetTokenInformation(hToken, TokenGroups, nullptr, 0, &bufferSize); if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { @@ -501,7 +501,7 @@ auto getLoggedInUsers() -> std::vector { WTSUserName, &buffer, &bytesReturned)) { if (buffer && bytesReturned > 1) { std::string username(buffer); - if (!username.empty() && + if (!username.empty() && std::find(users.begin(), users.end(), username) == users.end()) { users.push_back(username); spdlog::debug("Found logged-in user: {}", username); @@ -522,7 +522,7 @@ auto getLoggedInUsers() -> std::vector { while ((entry = getutent()) != nullptr) { if (entry->ut_type == USER_PROCESS) { std::string username(entry->ut_user); - if (!username.empty() && + if (!username.empty() && std::find(users.begin(), users.end(), username) == users.end()) { users.push_back(username); spdlog::debug("Found logged-in user: {}", username); diff --git a/atom/system/user.hpp b/atom/system/user.hpp index 0062cbad..7c8e29fe 100644 --- a/atom/system/user.hpp +++ b/atom/system/user.hpp @@ -132,4 +132,4 @@ ATOM_NODISCARD auto getLoggedInUsers() -> std::vector; ATOM_NODISCARD auto userExists(const std::string& username) -> bool; } // namespace atom::system -#endif \ No newline at end of file +#endif diff --git a/atom/system/virtual_network.cpp b/atom/system/virtual_network.cpp index cb1a127c..1dc2fa87 100644 --- a/atom/system/virtual_network.cpp +++ b/atom/system/virtual_network.cpp @@ -763,4 +763,4 @@ bool VirtualNetworkAdapter::ConfigureDNS(const std::wstring& adapterName, std::wstring VirtualNetworkAdapter::GetLastErrorMessage() const { return pImpl->GetLastErrorMessage(); -} \ No newline at end of file +} diff --git a/atom/system/virtual_network.hpp b/atom/system/virtual_network.hpp index f803117e..36ccf272 100644 --- a/atom/system/virtual_network.hpp +++ b/atom/system/virtual_network.hpp @@ -89,4 +89,4 @@ class VirtualNetworkAdapter { std::unique_ptr pImpl; }; -#endif // VIRTUAL_NETWORK_ADAPTER_H \ No newline at end of file +#endif // VIRTUAL_NETWORK_ADAPTER_H diff --git a/atom/system/voltage.cpp b/atom/system/voltage.cpp index 00f41a47..75cd3a22 100644 --- a/atom/system/voltage.cpp +++ b/atom/system/voltage.cpp @@ -63,4 +63,4 @@ std::unique_ptr VoltageMonitor::create() { #endif } -} // namespace atom::system \ No newline at end of file +} // namespace atom::system diff --git a/atom/system/voltage.hpp b/atom/system/voltage.hpp index 2fa65612..244beb5e 100644 --- a/atom/system/voltage.hpp +++ b/atom/system/voltage.hpp @@ -145,4 +145,4 @@ std::string powerSourceTypeToString(PowerSourceType type); } // namespace atom::system -#endif // ATOM_SYSTEM_VOLTAGE_HPP \ No newline at end of file +#endif // ATOM_SYSTEM_VOLTAGE_HPP diff --git a/atom/system/voltage_linux.cpp b/atom/system/voltage_linux.cpp index b00f32b8..282e98c1 100644 --- a/atom/system/voltage_linux.cpp +++ b/atom/system/voltage_linux.cpp @@ -167,4 +167,4 @@ inline std::unique_ptr VoltageMonitor::create() { } // namespace atom::system -#endif // __linux__ \ No newline at end of file +#endif // __linux__ diff --git a/atom/system/voltage_linux.hpp b/atom/system/voltage_linux.hpp index b88c6dd4..7656eb7e 100644 --- a/atom/system/voltage_linux.hpp +++ b/atom/system/voltage_linux.hpp @@ -92,4 +92,4 @@ class LinuxVoltageMonitor : public VoltageMonitor { } // namespace atom::system -#endif // __linux__ \ No newline at end of file +#endif // __linux__ diff --git a/atom/system/voltage_windows.cpp b/atom/system/voltage_windows.cpp index e4091217..71be662b 100644 --- a/atom/system/voltage_windows.cpp +++ b/atom/system/voltage_windows.cpp @@ -344,4 +344,4 @@ std::vector WindowsVoltageMonitor::getWMIPowerInfo() const { } // namespace atom::system -#endif // _WIN32 \ No newline at end of file +#endif // _WIN32 diff --git a/atom/system/voltage_windows.hpp b/atom/system/voltage_windows.hpp index a1ba3de2..ffe69c00 100644 --- a/atom/system/voltage_windows.hpp +++ b/atom/system/voltage_windows.hpp @@ -91,4 +91,4 @@ class WindowsVoltageMonitor : public VoltageMonitor { } // namespace atom::system -#endif // _WIN32 \ No newline at end of file +#endif // _WIN32 diff --git a/atom/system/xmake.lua b/atom/system/xmake.lua index 8c4fab24..8200e5fe 100644 --- a/atom/system/xmake.lua +++ b/atom/system/xmake.lua @@ -15,29 +15,29 @@ set_license("GPL3") -- Object Library target("atom-system-object") set_kind("object") - + -- Add source files add_files("*.cpp") add_files("module/*.cpp") - + -- Add header files add_headerfiles("*.hpp") add_headerfiles("module/*.hpp") - + -- Add dependencies add_packages("loguru") - + -- Add include directories add_includedirs(".", {public = true}) add_includedirs("..", {public = true}) - + -- Platform-specific settings if is_plat("linux") then add_syslinks("pthread") elseif is_plat("windows") then add_syslinks("pdh", "wlanapi") end - + -- Set C++ standard set_languages("c++20") target_end() @@ -46,22 +46,22 @@ target_end() target("atom-system") -- Set library type based on parent project option set_kind(has_config("shared_libs") and "shared" or "static") - + -- Add dependencies add_deps("atom-system-object") add_packages("loguru") - + -- Platform-specific settings if is_plat("linux") then add_syslinks("pthread") elseif is_plat("windows") then add_syslinks("pdh", "wlanapi") end - + -- Set output directories set_targetdir("$(buildir)/lib") set_objectdir("$(buildir)/obj") - + -- Install configuration on_install(function (target) os.cp(target:targetfile(), path.join(target:installdir(), "lib")) diff --git a/atom/tests/CMakeLists.txt b/atom/tests/CMakeLists.txt index 2449b5eb..105df2a3 100644 --- a/atom/tests/CMakeLists.txt +++ b/atom/tests/CMakeLists.txt @@ -60,4 +60,4 @@ install(TARGETS ${PROJECT_NAME} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} PUBLIC_HEADER DESTINATION include/${PROJECT_NAME} -) \ No newline at end of file +) diff --git a/atom/tests/benchmark.hpp b/atom/tests/benchmark.hpp index b7c718a7..68b500d9 100644 --- a/atom/tests/benchmark.hpp +++ b/atom/tests/benchmark.hpp @@ -93,13 +93,13 @@ class Benchmark { size_t peakUsage = 0; ///< Peak memory usage. MemoryStats() noexcept = default; - + /** * @brief Constructor with explicit initialization values * @param current Current memory usage * @param peak Peak memory usage */ - MemoryStats(size_t current, size_t peak) noexcept + MemoryStats(size_t current, size_t peak) noexcept : currentUsage(current), peakUsage(peak) {} /** diff --git a/atom/tests/charts.py b/atom/tests/charts.py index 312de3f2..e2c38a72 100644 --- a/atom/tests/charts.py +++ b/atom/tests/charts.py @@ -376,7 +376,7 @@ def generate_report(data, metrics, out_dir, style='default', dark_mode=False):

Performance Test Results

Generated on: {now}

- +

Statistics

""" @@ -416,17 +416,17 @@ def generate_report(data, metrics, out_dir, style='default', dark_mode=False):

{metric} - Bar Chart

{metric} Bar Chart
- +

{metric} - Line Chart

{metric} Line Chart
- +

{metric} - Pie Chart

{metric} Pie Chart
- +

{metric} - Histogram

{metric} Histogram diff --git a/atom/tests/perf.cpp b/atom/tests/perf.cpp index 7f82e181..9c8f8c8e 100644 --- a/atom/tests/perf.cpp +++ b/atom/tests/perf.cpp @@ -615,4 +615,4 @@ void Perf::finalize() { } spdlog::apply_all([](std::shared_ptr l) { l->flush(); }); -} \ No newline at end of file +} diff --git a/atom/tests/perf.hpp b/atom/tests/perf.hpp index ee1616e0..f46985c7 100644 --- a/atom/tests/perf.hpp +++ b/atom/tests/perf.hpp @@ -306,4 +306,4 @@ template auto measureWithTag(const char* tag, Func&& func, Args&&... args) { Perf p({__func__, __FILE__, __LINE__, tag}); return std::invoke(std::forward(func), std::forward(args)...); -} \ No newline at end of file +} diff --git a/atom/tests/test_cli.hpp b/atom/tests/test_cli.hpp index 6724dab5..4c46dd0c 100644 --- a/atom/tests/test_cli.hpp +++ b/atom/tests/test_cli.hpp @@ -481,4 +481,4 @@ class CommandLineParser { } // namespace atom::test -#endif // ATOM_TEST_CLI_HPP \ No newline at end of file +#endif // ATOM_TEST_CLI_HPP diff --git a/atom/tests/test_registry.hpp b/atom/tests/test_registry.hpp index 2870152d..1e97a1f7 100644 --- a/atom/tests/test_registry.hpp +++ b/atom/tests/test_registry.hpp @@ -310,4 +310,4 @@ inline void clearAllTests() { TestRegistry::instance().clear(); } } // namespace atom::test -#endif // ATOM_TEST_REGISTRY_HPP \ No newline at end of file +#endif // ATOM_TEST_REGISTRY_HPP diff --git a/atom/tests/test_reporter.hpp b/atom/tests/test_reporter.hpp index 051c85fa..4b5a3bdd 100644 --- a/atom/tests/test_reporter.hpp +++ b/atom/tests/test_reporter.hpp @@ -490,4 +490,4 @@ class HtmlReporter : public TestReporter { } // namespace atom::test -#endif // ATOM_TEST_REPORTER_HPP \ No newline at end of file +#endif // ATOM_TEST_REPORTER_HPP diff --git a/atom/tests/test_reporter_charts.hpp b/atom/tests/test_reporter_charts.hpp index 65aeaea2..6d8e69e6 100644 --- a/atom/tests/test_reporter_charts.hpp +++ b/atom/tests/test_reporter_charts.hpp @@ -423,4 +423,4 @@ class ChartReporter : public TestReporter { #endif // ATOM_USE_PYBIND11 -#endif // ATOM_TEST_REPORTER_CHARTS_HPP \ No newline at end of file +#endif // ATOM_TEST_REPORTER_CHARTS_HPP diff --git a/atom/tests/test_runner.hpp b/atom/tests/test_runner.hpp index 303079a6..6d5c08a2 100644 --- a/atom/tests/test_runner.hpp +++ b/atom/tests/test_runner.hpp @@ -755,4 +755,4 @@ class TestRunner { } // namespace atom::test -#endif // ATOM_TEST_RUNNER_HPP \ No newline at end of file +#endif // ATOM_TEST_RUNNER_HPP diff --git a/atom/type/args.hpp b/atom/type/args.hpp index 8a1f8b69..7f98109b 100644 --- a/atom/type/args.hpp +++ b/atom/type/args.hpp @@ -507,4 +507,4 @@ class Args { } // namespace atom -#endif // ATOM_TYPE_ARG_HPP \ No newline at end of file +#endif // ATOM_TYPE_ARG_HPP diff --git a/atom/type/argsview.hpp b/atom/type/argsview.hpp index bdb91c8a..89166d6f 100644 --- a/atom/type/argsview.hpp +++ b/atom/type/argsview.hpp @@ -573,4 +573,4 @@ void print(Args&&... args) { } // namespace atom #endif -#endif // ATOM_TYPE_ARGSVIEW_HPP \ No newline at end of file +#endif // ATOM_TYPE_ARGSVIEW_HPP diff --git a/atom/type/auto_table.hpp b/atom/type/auto_table.hpp index 903adfa4..87e81b18 100644 --- a/atom/type/auto_table.hpp +++ b/atom/type/auto_table.hpp @@ -515,4 +515,4 @@ void CountingHashTable::deserializeFromJson(const json& j) { } // namespace atom::type -#endif // ATOM_TYPE_COUNTING_HASH_TABLE_HPP \ No newline at end of file +#endif // ATOM_TYPE_COUNTING_HASH_TABLE_HPP diff --git a/atom/type/concurrent_map.hpp b/atom/type/concurrent_map.hpp index 7cf309fb..b92954a1 100644 --- a/atom/type/concurrent_map.hpp +++ b/atom/type/concurrent_map.hpp @@ -910,4 +910,4 @@ class concurrent_map { } // namespace atom::type -#endif // ATOM_TYPE_CONCURRENT_MAP_HPP \ No newline at end of file +#endif // ATOM_TYPE_CONCURRENT_MAP_HPP diff --git a/atom/type/concurrent_set.hpp b/atom/type/concurrent_set.hpp index 3649f502..4d1b174a 100644 --- a/atom/type/concurrent_set.hpp +++ b/atom/type/concurrent_set.hpp @@ -1461,4 +1461,4 @@ class concurrent_set { } // namespace atom::type -#endif // ATOM_TYPE_CONCURRENT_SET_HPP \ No newline at end of file +#endif // ATOM_TYPE_CONCURRENT_SET_HPP diff --git a/atom/type/expected.hpp b/atom/type/expected.hpp index 21d0c57f..94edfcda 100644 --- a/atom/type/expected.hpp +++ b/atom/type/expected.hpp @@ -133,7 +133,7 @@ class unexpected { std::is_nothrow_constructible_v) : error_(std::forward(error)) {} - + /** * @brief Gets a const reference to the error value. diff --git a/atom/type/json-schema.hpp b/atom/type/json-schema.hpp index 215bf9cd..2aebfd3e 100644 --- a/atom/type/json-schema.hpp +++ b/atom/type/json-schema.hpp @@ -1762,4 +1762,4 @@ class SchemaManager : public std::enable_shared_from_this { } // namespace atom::type -#endif // ATOM_TYPE_JSON_SCHEMA_HPP \ No newline at end of file +#endif // ATOM_TYPE_JSON_SCHEMA_HPP diff --git a/atom/type/json.hpp b/atom/type/json.hpp index 04944366..14de2792 100644 --- a/atom/type/json.hpp +++ b/atom/type/json.hpp @@ -24789,4 +24789,4 @@ inline void swap(nlohmann::NLOHMANN_BASIC_JSON_TPL& j1, nlohmann::NLOHMANN_BASIC -#endif // INCLUDE_NLOHMANN_JSON_HPP_ \ No newline at end of file +#endif // INCLUDE_NLOHMANN_JSON_HPP_ diff --git a/atom/type/json_fwd.hpp b/atom/type/json_fwd.hpp index 8fec46df..29a6036d 100644 --- a/atom/type/json_fwd.hpp +++ b/atom/type/json_fwd.hpp @@ -173,4 +173,4 @@ using ordered_json = basic_json; NLOHMANN_JSON_NAMESPACE_END -#endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_ \ No newline at end of file +#endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_ diff --git a/atom/type/noncopyable.hpp b/atom/type/noncopyable.hpp index 21b94328..72e0f251 100644 --- a/atom/type/noncopyable.hpp +++ b/atom/type/noncopyable.hpp @@ -48,4 +48,4 @@ class NonCopyable #endif }; -#endif // ATOM_TYPE_NONCOPYABLE_HPP \ No newline at end of file +#endif // ATOM_TYPE_NONCOPYABLE_HPP diff --git a/atom/type/pod_vector.hpp b/atom/type/pod_vector.hpp index 05c3e354..a3e68328 100644 --- a/atom/type/pod_vector.hpp +++ b/atom/type/pod_vector.hpp @@ -693,4 +693,4 @@ class PodVector { } // namespace atom::type -#endif // ATOM_TYPE_POD_VECTOR_HPP \ No newline at end of file +#endif // ATOM_TYPE_POD_VECTOR_HPP diff --git a/atom/type/qvariant.hpp b/atom/type/qvariant.hpp index 657810bd..c8598786 100644 --- a/atom/type/qvariant.hpp +++ b/atom/type/qvariant.hpp @@ -549,4 +549,4 @@ auto operator<<(std::ostream& outputStream, } // namespace atom::type -#endif \ No newline at end of file +#endif diff --git a/atom/type/robin_hood.hpp b/atom/type/robin_hood.hpp index d163e299..7bc7f129 100644 --- a/atom/type/robin_hood.hpp +++ b/atom/type/robin_hood.hpp @@ -515,4 +515,4 @@ class unordered_flat_map { } // namespace atom::utils -#endif \ No newline at end of file +#endif diff --git a/atom/type/ryaml.cpp b/atom/type/ryaml.cpp index 3d7bc185..90d04ad4 100644 --- a/atom/type/ryaml.cpp +++ b/atom/type/ryaml.cpp @@ -1558,4 +1558,4 @@ bool YamlParser::is_first_identifier_char(char c) { return std::isalpha(c) || c == '_'; } -} // namespace atom::type \ No newline at end of file +} // namespace atom::type diff --git a/atom/type/string.hpp b/atom/type/string.hpp index cc560c63..acccade1 100644 --- a/atom/type/string.hpp +++ b/atom/type/string.hpp @@ -1143,4 +1143,4 @@ struct hash { */ inline void swap(String& lhs, String& rhs) noexcept { lhs.swap(rhs); } -#endif // ATOM_TYPE_STRING_HPP \ No newline at end of file +#endif // ATOM_TYPE_STRING_HPP diff --git a/atom/type/trackable.hpp b/atom/type/trackable.hpp index 50b115b6..c826c47e 100644 --- a/atom/type/trackable.hpp +++ b/atom/type/trackable.hpp @@ -325,4 +325,4 @@ class Trackable { } }; -#endif // ATOM_TYPE_TRACKABLE_HPP \ No newline at end of file +#endif // ATOM_TYPE_TRACKABLE_HPP diff --git a/atom/type/uint.hpp b/atom/type/uint.hpp index e2152624..cb070a2f 100644 --- a/atom/type/uint.hpp +++ b/atom/type/uint.hpp @@ -74,4 +74,4 @@ constexpr auto operator""_u64(unsigned long long value) -> uint64_t { return static_cast(value); } -#endif // ATOM_TYPE_UINT_HPP \ No newline at end of file +#endif // ATOM_TYPE_UINT_HPP diff --git a/atom/type/weak_ptr.hpp b/atom/type/weak_ptr.hpp index 07c3b8f4..40b71764 100644 --- a/atom/type/weak_ptr.hpp +++ b/atom/type/weak_ptr.hpp @@ -811,4 +811,4 @@ template } // namespace atom::type -#endif // ATOM_TYPE_WEAK_PTR_HPP \ No newline at end of file +#endif // ATOM_TYPE_WEAK_PTR_HPP diff --git a/atom/type/xmake.lua b/atom/type/xmake.lua index fee077ee..709669f0 100644 --- a/atom/type/xmake.lua +++ b/atom/type/xmake.lua @@ -36,15 +36,15 @@ local headers = { -- Object Library target("atom-type-object") set_kind("object") - + -- Add files add_headerfiles(table.unpack(headers)) add_files(table.unpack(sources)) - + -- Add include directories add_includedirs(".", {public = true}) add_includedirs("..", {public = true}) - + -- Set C++ standard set_languages("c++20") target_end() @@ -53,17 +53,17 @@ target_end() target("atom-type") -- Set library type based on parent project option set_kind(has_config("shared_libs") and "shared" or "static") - + -- Add dependencies add_deps("atom-type-object", "atom-utils") - + -- Set include directories add_includedirs(".", {public = true}) - + -- Set output directories set_targetdir("$(buildir)/lib") set_objectdir("$(buildir)/obj") - + -- Install configuration on_install(function (target) os.cp(target:targetfile(), path.join(target:installdir(), "lib")) diff --git a/atom/utils/CMakeLists.txt b/atom/utils/CMakeLists.txt index 47927836..52182821 100644 --- a/atom/utils/CMakeLists.txt +++ b/atom/utils/CMakeLists.txt @@ -93,4 +93,4 @@ set_target_properties(${PROJECT_NAME} PROPERTIES # Installation install(TARGETS ${PROJECT_NAME} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} -) \ No newline at end of file +) diff --git a/atom/utils/aligned.hpp b/atom/utils/aligned.hpp index ada8f658..a8f767f8 100644 --- a/atom/utils/aligned.hpp +++ b/atom/utils/aligned.hpp @@ -61,4 +61,4 @@ class ValidateAlignedStorage { } // namespace atom::utils -#endif // ATOM_UTILS_ALIGNED_HPP \ No newline at end of file +#endif // ATOM_UTILS_ALIGNED_HPP diff --git a/atom/utils/bit.hpp b/atom/utils/bit.hpp index 20e109a3..33c0e108 100644 --- a/atom/utils/bit.hpp +++ b/atom/utils/bit.hpp @@ -498,4 +498,4 @@ auto parallelBitOp(std::span input, Op op) -> std::vector { } // namespace atom::utils -#endif // ATOM_UTILS_BIT_HPP \ No newline at end of file +#endif // ATOM_UTILS_BIT_HPP diff --git a/atom/utils/color_print.hpp b/atom/utils/color_print.hpp index 8bd9ddca..5b122843 100644 --- a/atom/utils/color_print.hpp +++ b/atom/utils/color_print.hpp @@ -195,4 +195,4 @@ using TextStyle = atom::utils::TextStyle; using ColorPrinter = atom::utils::ColorPrinter; } // namespace atom::test -#endif // ATOM_UTILS_COLOR_PRINT_HPP \ No newline at end of file +#endif // ATOM_UTILS_COLOR_PRINT_HPP diff --git a/atom/utils/convert.cpp b/atom/utils/convert.cpp index 23e033dc..2b507223 100644 --- a/atom/utils/convert.cpp +++ b/atom/utils/convert.cpp @@ -307,4 +307,4 @@ std::wstring LPCWSTRToWString(LPCWSTR lpcwstr) { } // namespace atom::utils -#endif \ No newline at end of file +#endif diff --git a/atom/utils/convert.hpp b/atom/utils/convert.hpp index 849188c6..b180e4a3 100644 --- a/atom/utils/convert.hpp +++ b/atom/utils/convert.hpp @@ -87,4 +87,4 @@ namespace atom::utils { } // namespace atom::utils #endif -#endif \ No newline at end of file +#endif diff --git a/atom/utils/lcg.cpp b/atom/utils/lcg.cpp index 799b3731..f7a4edb5 100644 --- a/atom/utils/lcg.cpp +++ b/atom/utils/lcg.cpp @@ -511,4 +511,4 @@ auto LCG::nextMultinomial(int trials, std::span probabilities) return counts; } -} // namespace atom::utils \ No newline at end of file +} // namespace atom::utils diff --git a/atom/utils/qprocess.cpp b/atom/utils/qprocess.cpp index 84c32d7a..b0b1771c 100644 --- a/atom/utils/qprocess.cpp +++ b/atom/utils/qprocess.cpp @@ -868,7 +868,7 @@ void QProcess::Impl::startAsyncReaders() { #else pollfd pfd = {childStdout_, POLLIN, 0}; int poll_result = poll(&pfd, 1, 100); - + if (poll_result > 0 && (pfd.revents & POLLIN)) { ssize_t bytesRead = ::read(childStdout_, buffer.data(), buffer.size()); if (bytesRead > 0) { @@ -907,7 +907,7 @@ void QProcess::Impl::startAsyncReaders() { #else pollfd pfd = {childStderr_, POLLIN, 0}; int poll_result = poll(&pfd, 1, 100); - + if (poll_result > 0 && (pfd.revents & POLLIN)) { ssize_t bytesRead = ::read(childStderr_, buffer.data(), buffer.size()); if (bytesRead > 0) { diff --git a/atom/utils/qtimer.cpp b/atom/utils/qtimer.cpp index 080aaf4c..0bc60646 100644 --- a/atom/utils/qtimer.cpp +++ b/atom/utils/qtimer.cpp @@ -338,4 +338,4 @@ void Timer::timerLoop() { } } -} // namespace atom::utils \ No newline at end of file +} // namespace atom::utils diff --git a/atom/utils/random.cpp b/atom/utils/random.cpp index b673d19b..bb8762b2 100644 --- a/atom/utils/random.cpp +++ b/atom/utils/random.cpp @@ -53,7 +53,7 @@ auto generateRandomString(int length, const std::string& charset, bool secure) - } } else { std::uniform_int_distribution dist(0, chars.size() - 1); - + for (int i = 0; i < length; ++i) { result.push_back(chars[dist(thread_engine)]); } diff --git a/atom/utils/simd_wrapper.hpp b/atom/utils/simd_wrapper.hpp index a64d997a..ae5a1742 100644 --- a/atom/utils/simd_wrapper.hpp +++ b/atom/utils/simd_wrapper.hpp @@ -643,4 +643,4 @@ using uint64x2_t = Vec; // 128位64位无符号整数向量 } // namespace simd -#endif // SIMD_WRAPPER_HPP \ No newline at end of file +#endif // SIMD_WRAPPER_HPP diff --git a/atom/utils/span.hpp b/atom/utils/span.hpp index 953bf7ca..233daf10 100644 --- a/atom/utils/span.hpp +++ b/atom/utils/span.hpp @@ -567,4 +567,4 @@ template } // namespace atom::utils -#endif // ATOM_UTILS_SPAN_HPP \ No newline at end of file +#endif // ATOM_UTILS_SPAN_HPP diff --git a/atom/utils/stopwatcher.cpp b/atom/utils/stopwatcher.cpp index dd135bdd..3668438f 100644 --- a/atom/utils/stopwatcher.cpp +++ b/atom/utils/stopwatcher.cpp @@ -710,4 +710,4 @@ auto StopWatcher::fromJson(std::string_view json) } } -} // namespace atom::utils \ No newline at end of file +} // namespace atom::utils diff --git a/atom/utils/stopwatcher.hpp b/atom/utils/stopwatcher.hpp index 05ee142a..f6d5cf07 100644 --- a/atom/utils/stopwatcher.hpp +++ b/atom/utils/stopwatcher.hpp @@ -309,4 +309,4 @@ class ScopedStopWatch { }; } // namespace atom::utils -#endif \ No newline at end of file +#endif diff --git a/atom/utils/switch.hpp b/atom/utils/switch.hpp index e468c94a..04c31e36 100644 --- a/atom/utils/switch.hpp +++ b/atom/utils/switch.hpp @@ -651,4 +651,4 @@ StringSwitch( } // namespace atom::utils -#endif // ATOM_UTILS_SWITCH_HPP \ No newline at end of file +#endif // ATOM_UTILS_SWITCH_HPP diff --git a/atom/utils/time.cpp b/atom/utils/time.cpp index 845ba4a0..86cd329a 100644 --- a/atom/utils/time.cpp +++ b/atom/utils/time.cpp @@ -426,4 +426,4 @@ auto timestampToTime(long long timestamp) -> std::optional { } } -} // namespace atom::utils \ No newline at end of file +} // namespace atom::utils diff --git a/atom/utils/to_byte.hpp b/atom/utils/to_byte.hpp index a12e4ce7..dfdb7dc3 100644 --- a/atom/utils/to_byte.hpp +++ b/atom/utils/to_byte.hpp @@ -783,4 +783,4 @@ inline auto loadFromFile(const std::string& filename) -> std::vector { } // namespace atom::utils -#endif // ATOM_UTILS_TO_BYTE_HPP \ No newline at end of file +#endif // ATOM_UTILS_TO_BYTE_HPP diff --git a/atom/utils/to_string.hpp b/atom/utils/to_string.hpp index 07ae59c5..a23afaee 100644 --- a/atom/utils/to_string.hpp +++ b/atom/utils/to_string.hpp @@ -201,7 +201,7 @@ template auto toString(const T& ptr) -> std::string { try { if (ptr) { - return std::format("SmartPointer({}, {})", + return std::format("SmartPointer({}, {})", static_cast(ptr.get()), toString(*ptr)); } return "nullptr"; @@ -287,7 +287,7 @@ auto toString(const T& container) -> std::string { * @throws ToStringException if conversion fails */ template - requires(!StringType && !Container && !PointerType && + requires(!StringType && !Container && !PointerType && !EnumType && !SmartPointer) auto toString(const T& value) -> std::string { try { @@ -323,11 +323,11 @@ auto joinCommandLine(const Args&... args) -> std::string { try { std::string result; result.reserve(sizeof...(args) * 32); - + bool first = true; - ((first ? (result += toString(args), first = false) + ((first ? (result += toString(args), first = false) : (result += " " + toString(args))), ...); - + return result; } catch (const std::exception& e) { throw ToStringException(std::format("Command line joining failed: {}", e.what())); @@ -432,7 +432,7 @@ auto toString(const std::array& array) -> std::string { * @throws ToStringException if conversion fails */ template -auto tupleToStringImpl(const Tuple& tpl, std::index_sequence, +auto tupleToStringImpl(const Tuple& tpl, std::index_sequence, std::string_view separator) -> std::string { try { std::string result = "("; diff --git a/atom/utils/valid_string.cpp b/atom/utils/valid_string.cpp index 85d6406a..8fe1704a 100644 --- a/atom/utils/valid_string.cpp +++ b/atom/utils/valid_string.cpp @@ -511,4 +511,4 @@ template auto validateImpl(const char*&&, const ValidationOptions&) template auto validateImpl(char*&&, const ValidationOptions&) -> std::expected; -} // namespace atom::utils \ No newline at end of file +} // namespace atom::utils diff --git a/atom/utils/xmake.lua b/atom/utils/xmake.lua index 5efa9807..6446b495 100644 --- a/atom/utils/xmake.lua +++ b/atom/utils/xmake.lua @@ -42,18 +42,18 @@ local headers = { -- Object Library target("atom-utils-object") set_kind("object") - + -- Add files add_headerfiles(table.unpack(headers)) add_files(table.unpack(sources)) - + -- Add dependencies add_packages("loguru", "tinyxml2") - + -- Add include directories add_includedirs(".", {public = true}) add_includedirs("..", {public = true}) - + -- Set C++ standard set_languages("c++20") target_end() @@ -62,18 +62,18 @@ target_end() target("atom-utils") -- Set library type based on parent project option set_kind(has_config("shared_libs") and "shared" or "static") - + -- Add dependencies add_deps("atom-utils-object") add_packages("loguru", "tinyxml2") - + -- Add include directories add_includedirs(".", {public = true}) - + -- Set output directories set_targetdir("$(buildir)/lib") set_objectdir("$(buildir)/obj") - + -- Install configuration on_install(function (target) os.cp(target:targetfile(), path.join(target:installdir(), "lib")) diff --git a/atom/utils/xml.cpp b/atom/utils/xml.cpp index cbc3855c..89dae3fc 100644 --- a/atom/utils/xml.cpp +++ b/atom/utils/xml.cpp @@ -355,4 +355,4 @@ auto XMLReader::isValidPath(std::string_view path) -> bool { return !path.empty(); // Placeholder } -} // namespace atom::utils \ No newline at end of file +} // namespace atom::utils diff --git a/atom/web/CMakeLists.txt b/atom/web/CMakeLists.txt index d4a67043..a4a0d8ea 100644 --- a/atom/web/CMakeLists.txt +++ b/atom/web/CMakeLists.txt @@ -70,7 +70,7 @@ set_property(TARGET ${PROJECT_NAME}_object PROPERTY POSITION_INDEPENDENT_CODE 1) target_link_libraries(${PROJECT_NAME}_object PRIVATE ${LIBS} atom-web-time) # Build Static Library -add_library(${PROJECT_NAME} STATIC +add_library(${PROJECT_NAME} STATIC $ $ $ @@ -88,4 +88,4 @@ set_target_properties(${PROJECT_NAME} PROPERTIES # Installation install(TARGETS ${PROJECT_NAME} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} -) \ No newline at end of file +) diff --git a/atom/web/address/CMakeLists.txt b/atom/web/address/CMakeLists.txt index 6be08dbc..69922e4a 100644 --- a/atom/web/address/CMakeLists.txt +++ b/atom/web/address/CMakeLists.txt @@ -44,4 +44,4 @@ target_include_directories(atom-web-address PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/. install(FILES ${ADDRESS_HEADERS} DESTINATION include/atom/web/address COMPONENT Development -) \ No newline at end of file +) diff --git a/atom/web/address/main.hpp b/atom/web/address/main.hpp index bf876b21..7de56e49 100644 --- a/atom/web/address/main.hpp +++ b/atom/web/address/main.hpp @@ -5,14 +5,14 @@ // 统一引入所有地址类型和相关功能 #include "address.hpp" -#include "ipv4.hpp" +#include "ipv4.hpp" #include "ipv6.hpp" #include "unix_domain.hpp" /** * @namespace atom::web * @brief 用于网络相关功能的命名空间 - * + * * 这个命名空间包含了各种网络地址类型和相关操作: * - IPv4 地址处理 * - IPv6 地址处理 @@ -26,7 +26,7 @@ namespace atom::web { * @brief 创建一个合适类型的地址对象 * @param addressString 地址字符串 (可以是IPv4, IPv6或Unix域套接字路径) * @return std::unique_ptr
指向创建的地址对象的智能指针 - * + * * 这是一个便捷函数,它会自动检测地址类型并创建相应的对象。 * 如果地址类型无法识别,将返回nullptr。 */ diff --git a/atom/web/curl.hpp b/atom/web/curl.hpp index aad627eb..6a381b9e 100644 --- a/atom/web/curl.hpp +++ b/atom/web/curl.hpp @@ -150,4 +150,4 @@ class CurlWrapper { } // namespace atom::web -#endif // ATOM_WEB_CURL_HPP \ No newline at end of file +#endif // ATOM_WEB_CURL_HPP diff --git a/atom/web/downloader.hpp b/atom/web/downloader.hpp index 53854faa..f18c7954 100644 --- a/atom/web/downloader.hpp +++ b/atom/web/downloader.hpp @@ -156,4 +156,4 @@ class DownloadManager { std::unique_ptr impl_; }; -} // namespace atom::web \ No newline at end of file +} // namespace atom::web diff --git a/atom/web/minetype.hpp b/atom/web/minetype.hpp index a104ae86..78a874d4 100644 --- a/atom/web/minetype.hpp +++ b/atom/web/minetype.hpp @@ -188,4 +188,4 @@ class MimeTypes { std::unique_ptr pImpl; }; -#endif // ATOM_WEB_MIMETYPE_HPP \ No newline at end of file +#endif // ATOM_WEB_MIMETYPE_HPP diff --git a/atom/web/time/xmake.lua b/atom/web/time/xmake.lua index d17f5694..ece385f8 100644 --- a/atom/web/time/xmake.lua +++ b/atom/web/time/xmake.lua @@ -21,22 +21,22 @@ local time_headers = { -- Build Object Library target("atom-web-time-object") set_kind("object") - + -- Add files add_headerfiles(table.unpack(time_headers)) add_files(table.unpack(time_sources)) - + -- Add dependencies add_packages("loguru") - + -- Add include directories add_includedirs("$(projectdir)/atom", {public = true}) - + -- Platform-specific settings if is_plat("windows") then add_syslinks("wsock32", "ws2_32") end - + -- Set C++ standard set_languages("c++20") target_end() diff --git a/atom/web/utils/dns.hpp b/atom/web/utils/dns.hpp index 1563273a..80a723b5 100644 --- a/atom/web/utils/dns.hpp +++ b/atom/web/utils/dns.hpp @@ -23,7 +23,7 @@ namespace atom::web { /** * @brief Set the Time-To-Live for DNS cache entries - * + * * @param ttlSeconds The TTL in seconds */ void setDNSCacheTTL(std::chrono::seconds ttlSeconds); diff --git a/atom/web/xmake.lua b/atom/web/xmake.lua index 506d6097..465763c3 100644 --- a/atom/web/xmake.lua +++ b/atom/web/xmake.lua @@ -59,18 +59,18 @@ end -- Object Library target("atom-web-object") set_kind("object") - + -- Add files add_headerfiles(table.unpack(headers)) add_files(table.unpack(sources)) - + -- Add dependencies add_packages("loguru") - + -- Add include directories add_includedirs(".", {public = true}) add_includedirs("..", {public = true}) - + -- Set C++ standard set_languages("c++20") target_end() @@ -79,23 +79,23 @@ target_end() target("atom-web") -- Set library type based on parent project option set_kind(has_config("shared_libs") and "shared" or "static") - + -- Add dependencies add_deps("atom-web-object") add_packages("loguru", "cpp-httplib") - + -- Add include directories add_includedirs(".", {public = true}) - + -- Platform-specific settings if is_plat("windows") then add_syslinks("wsock32", "ws2_32") end - + -- Set output directories set_targetdir("$(buildir)/lib") set_objectdir("$(buildir)/obj") - + -- Install configuration on_install(function (target) os.cp(target:targetfile(), path.join(target:installdir(), "lib")) diff --git a/atom/xmake.lua b/atom/xmake.lua index ea242dd1..81e0eae6 100644 --- a/atom/xmake.lua +++ b/atom/xmake.lua @@ -33,8 +33,8 @@ option_end() -- Module build options local modules = { - "algorithm", "async", "components", "connection", "containers", - "error", "io", "log", "memory", "meta", "search", "secret", + "algorithm", "async", "components", "connection", "containers", + "error", "io", "log", "memory", "meta", "search", "secret", "serial", "sysinfo", "system", "type", "utils", "web" } @@ -66,7 +66,7 @@ option_end() if has_config("python") then add_requires("python3", "pybind11") - + after_load(function () local python = find_tool("python3") if python then @@ -94,7 +94,7 @@ end function check_module_directory(name, dir_name) local module_path = path.join(".", dir_name) local xmake_file = path.join(module_path, "xmake.lua") - + if os.isdir(module_path) and os.isfile(xmake_file) then return true else @@ -161,16 +161,16 @@ end if has_config("unified") and #_G.ATOM_MODULES > 0 then target("atom-unified") set_kind("phony") - + -- Add all module dependencies for _, module in ipairs(_G.ATOM_MODULES) do add_deps(module) end - + after_build(function (target) print("Created unified Atom library with modules: " .. table.concat(_G.ATOM_MODULES, ", ")) end) - + -- Create atom alias target target("atom") set_kind("phony") @@ -187,7 +187,7 @@ task("install-all") usage = "xmake install-all", description = "Install all Atom modules" } - + on_run(function () for _, module in ipairs(_G.ATOM_MODULES) do os.exec("xmake install " .. module) diff --git a/build.bat b/build.bat index 18176962..6d3d5590 100644 --- a/build.bat +++ b/build.bat @@ -135,7 +135,7 @@ if "%CLEAN_BUILD%"=="y" ( REM Build using the selected system if "%BUILD_SYSTEM%"=="xmake" ( echo Building with XMake... - + REM Configure XMake options set XMAKE_ARGS= if "%BUILD_TYPE%"=="debug" set XMAKE_ARGS=%XMAKE_ARGS% -m debug @@ -145,7 +145,7 @@ if "%BUILD_SYSTEM%"=="xmake" ( if "%BUILD_TESTS%"=="y" set XMAKE_ARGS=%XMAKE_ARGS% --tests=y if "%BUILD_CFITSIO%"=="y" set XMAKE_ARGS=%XMAKE_ARGS% --cfitsio=y if "%BUILD_SSH%"=="y" set XMAKE_ARGS=%XMAKE_ARGS% --ssh=y - + REM Run XMake echo Configuring XMake project... xmake f %XMAKE_ARGS% @@ -153,7 +153,7 @@ if "%BUILD_SYSTEM%"=="xmake" ( echo Error: XMake configuration failed exit /b 1 ) - + echo Building project... xmake if %ERRORLEVEL% NEQ 0 ( @@ -162,7 +162,7 @@ if "%BUILD_SYSTEM%"=="xmake" ( ) ) else ( echo Building with CMake... - + REM Configure CMake options set CMAKE_ARGS=-B build if "%BUILD_TYPE%"=="debug" set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_BUILD_TYPE=Debug @@ -173,7 +173,7 @@ if "%BUILD_SYSTEM%"=="xmake" ( if "%BUILD_TESTS%"=="y" set CMAKE_ARGS=%CMAKE_ARGS% -DATOM_BUILD_TESTS=ON if "%BUILD_CFITSIO%"=="y" set CMAKE_ARGS=%CMAKE_ARGS% -DATOM_USE_CFITSIO=ON if "%BUILD_SSH%"=="y" set CMAKE_ARGS=%CMAKE_ARGS% -DATOM_USE_SSH=ON - + REM Run CMake echo Configuring CMake project... cmake %CMAKE_ARGS% . @@ -181,7 +181,7 @@ if "%BUILD_SYSTEM%"=="xmake" ( echo Error: CMake configuration failed exit /b 1 ) - + echo Building project... cmake --build build --config %BUILD_TYPE% if %ERRORLEVEL% NEQ 0 ( diff --git a/build.sh b/build.sh index 4ade7a83..532d7e45 100644 --- a/build.sh +++ b/build.sh @@ -130,7 +130,7 @@ fi # Build using the selected system if [[ "$BUILD_SYSTEM" == "xmake" ]]; then echo "Building with XMake..." - + # Configure XMake options XMAKE_ARGS="" if [[ "$BUILD_TYPE" == "debug" ]]; then XMAKE_ARGS="$XMAKE_ARGS -m debug"; fi @@ -140,7 +140,7 @@ if [[ "$BUILD_SYSTEM" == "xmake" ]]; then if [[ "$BUILD_TESTS" == "y" ]]; then XMAKE_ARGS="$XMAKE_ARGS --tests=y"; fi if [[ "$BUILD_CFITSIO" == "y" ]]; then XMAKE_ARGS="$XMAKE_ARGS --cfitsio=y"; fi if [[ "$BUILD_SSH" == "y" ]]; then XMAKE_ARGS="$XMAKE_ARGS --ssh=y"; fi - + # Run XMake echo "Configuring XMake project..." xmake f $XMAKE_ARGS @@ -148,7 +148,7 @@ if [[ "$BUILD_SYSTEM" == "xmake" ]]; then echo "Error: XMake configuration failed" exit 1 fi - + echo "Building project..." xmake if [ $? -ne 0 ]; then @@ -157,7 +157,7 @@ if [[ "$BUILD_SYSTEM" == "xmake" ]]; then fi else echo "Building with CMake..." - + # Configure CMake options CMAKE_ARGS="-B build" if [[ "$BUILD_TYPE" == "debug" ]]; then CMAKE_ARGS="$CMAKE_ARGS -DCMAKE_BUILD_TYPE=Debug"; fi @@ -168,7 +168,7 @@ else if [[ "$BUILD_TESTS" == "y" ]]; then CMAKE_ARGS="$CMAKE_ARGS -DATOM_BUILD_TESTS=ON"; fi if [[ "$BUILD_CFITSIO" == "y" ]]; then CMAKE_ARGS="$CMAKE_ARGS -DATOM_USE_CFITSIO=ON"; fi if [[ "$BUILD_SSH" == "y" ]]; then CMAKE_ARGS="$CMAKE_ARGS -DATOM_USE_SSH=ON"; fi - + # Run CMake echo "Configuring CMake project..." cmake $CMAKE_ARGS . @@ -176,7 +176,7 @@ else echo "Error: CMake configuration failed" exit 1 fi - + # Determine number of CPU cores for parallel build if command -v nproc &> /dev/null; then CORES=$(nproc) @@ -185,7 +185,7 @@ else else CORES=4 # Default to 4 cores if we can't determine fi - + echo "Building project using $CORES cores..." cmake --build build --config $BUILD_TYPE --parallel $CORES if [ $? -ne 0 ]; then diff --git a/cmake/ExamplesBuildOptions.cmake b/cmake/ExamplesBuildOptions.cmake index db32f4cb..1472712c 100644 --- a/cmake/ExamplesBuildOptions.cmake +++ b/cmake/ExamplesBuildOptions.cmake @@ -1,7 +1,7 @@ # ExamplesBuildOptions.cmake # # This file contains all options for controlling the build of Atom examples -# +# # Author: Max Qian # License: GPL3 diff --git a/cmake/FindAsio.cmake b/cmake/FindAsio.cmake index 6aa02f2a..951b9b84 100644 --- a/cmake/FindAsio.cmake +++ b/cmake/FindAsio.cmake @@ -4,14 +4,14 @@ if(ASIO_INCLUDE_DIR) set(Asio_FOUND TRUE) set(ASIO_STANDALONE TRUE) set(ASIO_INCLUDE_DIRS ${ASIO_INCLUDE_DIR}) - + if(NOT TARGET Asio::Asio) add_library(Asio::Asio INTERFACE IMPORTED) set_target_properties(Asio::Asio PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${ASIO_INCLUDE_DIRS}" INTERFACE_COMPILE_DEFINITIONS "ASIO_STANDALONE") endif() - + mark_as_advanced(ASIO_INCLUDE_DIR) else() find_package(Boost QUIET COMPONENTS system) @@ -21,7 +21,7 @@ else() set(Asio_FOUND TRUE) set(ASIO_STANDALONE FALSE) set(ASIO_INCLUDE_DIRS ${Boost_INCLUDE_DIRS}) - + if(NOT TARGET Asio::Asio) add_library(Asio::Asio INTERFACE IMPORTED) set_target_properties(Asio::Asio PROPERTIES diff --git a/cmake/FindReadline.cmake b/cmake/FindReadline.cmake index 87a9e977..a2ced20d 100644 --- a/cmake/FindReadline.cmake +++ b/cmake/FindReadline.cmake @@ -12,11 +12,11 @@ endif() # **Windows specific search paths** if(WIN32) # Native Windows paths - list(APPEND CMAKE_PREFIX_PATH + list(APPEND CMAKE_PREFIX_PATH "C:/Program Files/readline" "C:/readline" ) - + # **MSYS2 environment paths** # First, try to get MSYS2 paths from the PATH environment variable set(_msys_prefixes_from_env_path "") @@ -24,7 +24,7 @@ if(WIN32) set(_path_list "$ENV{PATH}") string(REPLACE ";" "\\;" _path_list "${_path_list}") string(REPLACE "\\" "/" _path_list "${_path_list}") - + if(WIN32) string(REPLACE ";" "\\\\;" _path_list_escaped "${_path_list}") string(REPLACE "\\\\;" ";" _path_list_escaped "${_path_list_escaped}") @@ -33,10 +33,10 @@ if(WIN32) else() string(REPLACE ":" ";" _path_list_cmake "${_path_list}") endif() - + foreach(_path_entry IN LISTS _path_list_cmake) string(REPLACE "\\" "/" _path_entry "${_path_entry}") - + if(_path_entry MATCHES ".*/mingw64/bin$") get_filename_component(_prefix_mingw64 "${_path_entry}" DIRECTORY) list(APPEND _msys_prefixes_from_env_path "${_prefix_mingw64}") @@ -71,7 +71,7 @@ if(WIN32) endif() endif() endforeach() - + if(_msys_prefixes_from_env_path) list(REMOVE_DUPLICATES _msys_prefixes_from_env_path) list(APPEND CMAKE_PREFIX_PATH ${_msys_prefixes_from_env_path}) @@ -93,7 +93,7 @@ if(WIN32) endif() else() # Finally, check common MSYS2 installation paths - list(APPEND CMAKE_PREFIX_PATH + list(APPEND CMAKE_PREFIX_PATH "D:/msys64/mingw64" "D:/msys64/mingw32" "D:/msys64/usr" @@ -114,9 +114,9 @@ endif() # Find include directory find_path(Readline_INCLUDE_DIR NAMES readline/readline.h - PATHS + PATHS ${READLINE_ROOT}/include - /usr/include + /usr/include /usr/local/include /opt/local/include /sw/include @@ -127,30 +127,30 @@ find_path(Readline_INCLUDE_DIR if(WIN32) find_library(Readline_LIBRARY NAMES readline libreadline readline.lib - PATHS + PATHS ${READLINE_ROOT}/lib - /usr/lib + /usr/lib /usr/local/lib /opt/local/lib /sw/lib ) - + # **On Windows/MSYS2, Readline often depends on ncurses or termcap** find_library(Readline_NCURSES_LIBRARY NAMES ncurses libncurses ncursesw libncursesw pdcurses - PATHS + PATHS ${READLINE_ROOT}/lib - /usr/lib + /usr/lib /usr/local/lib /opt/local/lib /sw/lib ) - + find_library(Readline_TERMCAP_LIBRARY NAMES termcap libtermcap - PATHS + PATHS ${READLINE_ROOT}/lib - /usr/lib + /usr/lib /usr/local/lib /opt/local/lib /sw/lib @@ -158,9 +158,9 @@ if(WIN32) else() find_library(Readline_LIBRARY NAMES readline - PATHS + PATHS ${READLINE_ROOT}/lib - /usr/lib + /usr/lib /usr/local/lib /opt/local/lib /sw/lib @@ -188,7 +188,7 @@ if(Readline_FOUND) endif() mark_as_advanced( - Readline_INCLUDE_DIR + Readline_INCLUDE_DIR Readline_LIBRARY Readline_NCURSES_LIBRARY Readline_TERMCAP_LIBRARY diff --git a/cmake/FindYamlCpp.cmake b/cmake/FindYamlCpp.cmake index f7170cfb..888b4594 100644 --- a/cmake/FindYamlCpp.cmake +++ b/cmake/FindYamlCpp.cmake @@ -39,7 +39,7 @@ if(WIN32) if(MSVC) set(_YAMLCPP_MSVC_SUFFIX "md" CACHE STRING "基于运行时库选择的 yaml-cpp 库后缀") if(CMAKE_BUILD_TYPE STREQUAL "Debug") - set(_YAMLCPP_LIB_NAMES + set(_YAMLCPP_LIB_NAMES yaml-cppd libyaml-cppd yaml-cpp${_YAMLCPP_MSVC_SUFFIX}d @@ -47,7 +47,7 @@ if(WIN32) yaml-cpp libyaml-cpp) else() - set(_YAMLCPP_LIB_NAMES + set(_YAMLCPP_LIB_NAMES yaml-cpp libyaml-cpp yaml-cpp${_YAMLCPP_MSVC_SUFFIX} @@ -76,11 +76,11 @@ endif() # 尝试查找包含目录 find_path(YAMLCPP_INCLUDE_DIR NAMES yaml-cpp/yaml.h - HINTS + HINTS ${PC_YAMLCPP_INCLUDEDIR} ${PC_YAMLCPP_INCLUDE_DIRS} ${_YAMLCPP_POSSIBLE_ROOT_DIRS} - PATH_SUFFIXES + PATH_SUFFIXES include yaml-cpp/include ) @@ -88,11 +88,11 @@ find_path(YAMLCPP_INCLUDE_DIR # 尝试查找库文件 find_library(YAMLCPP_LIBRARY NAMES ${_YAMLCPP_LIB_NAMES} - HINTS + HINTS ${PC_YAMLCPP_LIBDIR} ${PC_YAMLCPP_LIBRARY_DIRS} ${_YAMLCPP_POSSIBLE_ROOT_DIRS} - PATH_SUFFIXES + PATH_SUFFIXES lib lib64 lib/${CMAKE_LIBRARY_ARCHITECTURE} @@ -107,12 +107,12 @@ if(YAMLCPP_INCLUDE_DIR AND NOT YAMLCPP_VERSION) string(REGEX MATCH "#define YAML_CPP_VERSION_MAJOR ([0-9]+)" _YAMLCPP_MAJOR_VERSION_MATCH "${_YAMLCPP_VERSION_HEADER}") string(REGEX MATCH "#define YAML_CPP_VERSION_MINOR ([0-9]+)" _YAMLCPP_MINOR_VERSION_MATCH "${_YAMLCPP_VERSION_HEADER}") string(REGEX MATCH "#define YAML_CPP_VERSION_PATCH ([0-9]+)" _YAMLCPP_PATCH_VERSION_MATCH "${_YAMLCPP_VERSION_HEADER}") - + if(_YAMLCPP_MAJOR_VERSION_MATCH AND _YAMLCPP_MINOR_VERSION_MATCH AND _YAMLCPP_PATCH_VERSION_MATCH) string(REGEX REPLACE "#define YAML_CPP_VERSION_MAJOR ([0-9]+)" "\\1" _YAMLCPP_MAJOR_VERSION "${_YAMLCPP_MAJOR_VERSION_MATCH}") string(REGEX REPLACE "#define YAML_CPP_VERSION_MINOR ([0-9]+)" "\\1" _YAMLCPP_MINOR_VERSION "${_YAMLCPP_MINOR_VERSION_MATCH}") string(REGEX REPLACE "#define YAML_CPP_VERSION_PATCH ([0-9]+)" "\\1" _YAMLCPP_PATCH_VERSION "${_YAMLCPP_PATCH_VERSION_MATCH}") - + set(YAMLCPP_VERSION "${_YAMLCPP_MAJOR_VERSION}.${_YAMLCPP_MINOR_VERSION}.${_YAMLCPP_PATCH_VERSION}") endif() endif() diff --git a/cmake/GitVersion.cmake b/cmake/GitVersion.cmake index e355a8b9..b538ac49 100644 --- a/cmake/GitVersion.cmake +++ b/cmake/GitVersion.cmake @@ -10,15 +10,15 @@ function(configure_version_from_git) if(NOT DEFINED ARG_OUTPUT_HEADER) set(ARG_OUTPUT_HEADER "${CMAKE_CURRENT_BINARY_DIR}/version.h") endif() - + if(NOT DEFINED ARG_VERSION_VARIABLE) set(ARG_VERSION_VARIABLE PROJECT_VERSION) endif() - + if(NOT DEFINED ARG_PREFIX) set(ARG_PREFIX "${PROJECT_NAME}") endif() - + # Get Git information find_package(Git QUIET) if(GIT_FOUND) @@ -30,7 +30,7 @@ function(configure_version_from_git) OUTPUT_QUIET ERROR_QUIET ) - + if(GIT_REPO_CHECK EQUAL 0) # Get the most recent tag execute_process( @@ -41,7 +41,7 @@ function(configure_version_from_git) ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE ) - + # Get the current commit short hash execute_process( COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD @@ -51,7 +51,7 @@ function(configure_version_from_git) ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE ) - + # Get the number of commits since the most recent tag execute_process( COMMAND ${GIT_EXECUTABLE} rev-list --count ${GIT_TAG}..HEAD @@ -61,20 +61,20 @@ function(configure_version_from_git) ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE ) - + # Check if the working directory is clean execute_process( COMMAND ${GIT_EXECUTABLE} diff --quiet HEAD WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} RESULT_VARIABLE GIT_DIRTY_RESULT ) - + if(NOT GIT_DIRTY_RESULT EQUAL 0) set(GIT_DIRTY "-dirty") else() set(GIT_DIRTY "") endif() - + # Build version string if(GIT_TAG_RESULT EQUAL 0) # Parse tag version number (assuming format vX.Y.Z or X.Y.Z) @@ -83,12 +83,12 @@ function(configure_version_from_git) set(VERSION_MAJOR ${CMAKE_MATCH_1}) set(VERSION_MINOR ${CMAKE_MATCH_2}) set(VERSION_PATCH ${CMAKE_MATCH_3}) - + # If there are additional commits, increment the patch version if(GIT_COUNT GREATER 0) math(EXPR VERSION_PATCH "${VERSION_PATCH}+${GIT_COUNT}") endif() - + set(VERSION_STRING "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}${GIT_DIRTY}") else() set(VERSION_STRING "${GIT_TAG}-${GIT_HASH}${GIT_DIRTY}") @@ -103,7 +103,7 @@ function(configure_version_from_git) set(VERSION_MINOR 0) set(VERSION_PATCH 0) endif() - + # Set variables in parent scope set(${ARG_VERSION_VARIABLE} "${VERSION_STRING}" PARENT_SCOPE) set(PROJECT_VERSION_MAJOR ${VERSION_MAJOR} PARENT_SCOPE) @@ -111,10 +111,10 @@ function(configure_version_from_git) set(PROJECT_VERSION_PATCH ${VERSION_PATCH} PARENT_SCOPE) set(GIT_HASH ${GIT_HASH} PARENT_SCOPE) set(GIT_DIRTY_RESULT ${GIT_DIRTY_RESULT} PARENT_SCOPE) - + # Generate version header file string(TOUPPER "${ARG_PREFIX}" PREFIX_UPPER) - + # Configure using the existing version.h.in template set(VERSION_TEMPLATE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/version_info.h.in") if(EXISTS "${VERSION_TEMPLATE_PATH}") @@ -126,7 +126,7 @@ function(configure_version_from_git) message(STATUS "Generated version header file from template: ${VERSION_TEMPLATE_PATH} -> ${ARG_OUTPUT_HEADER}") else() message(WARNING "Version template file not found: ${VERSION_TEMPLATE_PATH}") - + # Fall back to built-in template message(STATUS "Creating a default version header file at: ${ARG_OUTPUT_HEADER}") configure_file( @@ -135,7 +135,7 @@ function(configure_version_from_git) @ONLY ) endif() - + message(STATUS "Git version: ${VERSION_STRING} (Major: ${VERSION_MAJOR}, Minor: ${VERSION_MINOR}, Patch: ${VERSION_PATCH}, Hash: ${GIT_HASH})") else() message(WARNING "Current directory is not a Git repository, using default version") @@ -171,17 +171,17 @@ function(configure_atom_version) VERSION_VARIABLE ${ARG_VERSION_VARIABLE} PREFIX "ATOM" ) - + # Now generate the user-friendly version info header if(${ARG_VERSION_VARIABLE}) set(PROJECT_VERSION ${${ARG_VERSION_VARIABLE}}) endif() - + configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/cmake/version_info.h.in" "${CMAKE_CURRENT_BINARY_DIR}/atom_version_info.h" @ONLY ) - + message(STATUS "Generated atom_version_info.h with version ${PROJECT_VERSION}") -endfunction() \ No newline at end of file +endfunction() diff --git a/cmake/PlatformSpecifics.cmake b/cmake/PlatformSpecifics.cmake index 0a4e77a3..ec24ee86 100644 --- a/cmake/PlatformSpecifics.cmake +++ b/cmake/PlatformSpecifics.cmake @@ -117,4 +117,4 @@ if(UNIX AND NOT APPLE) else() message(WARNING "ccache not found: compiler cache support disabled.\nRecommendation: On Linux, you can install ccache via package manager, e.g.: sudo apt install ccache or sudo yum install ccache") endif() -endif() \ No newline at end of file +endif() diff --git a/cmake/ScanModule.cmake b/cmake/ScanModule.cmake index 627c00ad..c74bc87a 100644 --- a/cmake/ScanModule.cmake +++ b/cmake/ScanModule.cmake @@ -5,24 +5,24 @@ function(scan_and_generate_modules source_dir return_var) set(modules_name_r "") file(GLOB_RECURSE CPP_FILES "${source_dir}/*.cpp") - + foreach(cpp_file ${CPP_FILES}) file(READ ${cpp_file} file_content) string(REGEX MATCH "ATOM_MODULE\\(([a-zA-Z0-9_]+)," match ${file_content}) - + if(match) string(REGEX REPLACE "ATOM_MODULE\\(([a-zA-Z0-9_]+),.*" "\\1" module_name ${match}) - + if(NOT module_name) message(WARNING "Found ATOM_MODULE macro in ${cpp_file} but could not extract module name.") continue() endif() - + set(modules_name_r ${module_name}) message(VERBOSE "Found module '${module_name}' in ${cpp_file}") endif() endforeach() - + set(${return_var} "${modules_name_r}" PARENT_SCOPE) endfunction() @@ -34,38 +34,38 @@ endfunction() function(scan_module_dependencies) # Find all enabled modules set(enabled_modules) - + # Map build options to module names if(ATOM_BUILD_ERROR) list(APPEND enabled_modules "atom-error") message(STATUS "Module 'atom-error' is enabled") endif() - + if(ATOM_BUILD_LOG) list(APPEND enabled_modules "atom-log") message(STATUS "Module 'atom-log' is enabled") endif() - + if(ATOM_BUILD_ALGORITHM) list(APPEND enabled_modules "atom-algorithm") message(STATUS "Module 'atom-algorithm' is enabled") endif() - + if(ATOM_BUILD_ASYNC) list(APPEND enabled_modules "atom-async") message(STATUS "Module 'atom-async' is enabled") endif() - + if(ATOM_BUILD_COMPONENTS) list(APPEND enabled_modules "atom-components") message(STATUS "Module 'atom-components' is enabled") endif() - + if(ATOM_BUILD_CONNECTION) list(APPEND enabled_modules "atom-connection") message(STATUS "Module 'atom-connection' is enabled") endif() - + if(ATOM_BUILD_CONTAINERS) list(APPEND enabled_modules "atom-containers") message(STATUS "Module 'atom-containers' is enabled") @@ -75,52 +75,52 @@ function(scan_module_dependencies) list(APPEND enabled_modules "atom-io") message(STATUS "Module 'atom-io' is enabled") endif() - + if(ATOM_BUILD_META) list(APPEND enabled_modules "atom-meta") message(STATUS "Module 'atom-meta' is enabled") endif() - + if(ATOM_BUILD_MEMORY) list(APPEND enabled_modules "atom-memory") message(STATUS "Module 'atom-memory' is enabled") endif() - + if(ATOM_BUILD_SEARCH) list(APPEND enabled_modules "atom-search") message(STATUS "Module 'atom-search' is enabled") endif() - + if(ATOM_BUILD_SECRET) list(APPEND enabled_modules "atom-secret") message(STATUS "Module 'atom-secret' is enabled") endif() - + if(ATOM_BUILD_SERIAL) list(APPEND enabled_modules "atom-serial") message(STATUS "Module 'atom-serial' is enabled") endif() - + if(ATOM_BUILD_SYSINFO) list(APPEND enabled_modules "atom-sysinfo") message(STATUS "Module 'atom-sysinfo' is enabled") endif() - + if(ATOM_BUILD_SYSTEM) list(APPEND enabled_modules "atom-system") message(STATUS "Module 'atom-system' is enabled") endif() - + if(ATOM_BUILD_TYPE) list(APPEND enabled_modules "atom-type") message(STATUS "Module 'atom-type' is enabled") endif() - + if(ATOM_BUILD_UTILS) list(APPEND enabled_modules "atom-utils") message(STATUS "Module 'atom-utils' is enabled") endif() - + if(ATOM_BUILD_WEB) list(APPEND enabled_modules "atom-web") message(STATUS "Module 'atom-web' is enabled") @@ -135,7 +135,7 @@ endfunction() function(module_exists module_name result_var) # Convert module name (e.g., "atom-error") to directory name (e.g., "error") string(REPLACE "atom-" "" dir_name "${module_name}") - + # Check if directory exists and has a CMakeLists.txt file set(module_path "${CMAKE_CURRENT_SOURCE_DIR}/../atom/${dir_name}") if(EXISTS "${module_path}" AND EXISTS "${module_path}/CMakeLists.txt") @@ -149,10 +149,10 @@ endfunction() function(process_module_dependencies) # Get list of initially enabled modules get_property(enabled_modules GLOBAL PROPERTY ATOM_ENABLED_MODULES) - + # Create a copy of the initial list set(initial_modules ${enabled_modules}) - + # Validate initial modules - remove any that don't exist set(validated_modules "") foreach(module ${enabled_modules}) @@ -167,32 +167,32 @@ function(process_module_dependencies) set(ATOM_BUILD_${module_upper} OFF CACHE BOOL "Build ${module} module" FORCE) endif() endforeach() - + # Update the enabled modules list with only valid ones set(enabled_modules ${validated_modules}) set_property(GLOBAL PROPERTY ATOM_ENABLED_MODULES "${enabled_modules}") - + # Process dependencies until no new modules are added set(process_again TRUE) set(iteration 0) set(max_iterations 10) # Prevent infinite loops - + while(process_again AND iteration LESS max_iterations) set(process_again FALSE) set(new_modules "") - + # For each enabled module, check its dependencies foreach(module ${enabled_modules}) # Convert module name to uppercase for variable lookup string(TOUPPER "${module}" module_upper) string(REPLACE "-" "_" module_var "${module_upper}") - + # Get dependencies for this module if(DEFINED ATOM_${module_var}_DEPENDS) foreach(dep ${ATOM_${module_var}_DEPENDS}) # Check if dependency exists before adding it module_exists(${dep} DEP_EXISTS) - + # If the dependency is not already in the enabled list and it exists, add it if(NOT "${dep}" IN_LIST enabled_modules AND DEP_EXISTS) list(APPEND new_modules ${dep}) @@ -206,21 +206,21 @@ function(process_module_dependencies) endforeach() endif() endforeach() - + # Add newly discovered dependencies to the enabled list if(new_modules) list(APPEND enabled_modules ${new_modules}) list(REMOVE_DUPLICATES enabled_modules) endif() - + math(EXPR iteration "${iteration} + 1") endwhile() - + # Check if we reached max iterations if(iteration EQUAL max_iterations) message(WARNING "Reached maximum dependency resolution iterations. There may be circular dependencies.") endif() - + # Find any new modules that were added because of dependencies set(added_modules "") foreach(module ${enabled_modules}) @@ -228,24 +228,24 @@ function(process_module_dependencies) list(APPEND added_modules ${module}) endif() endforeach() - + if(added_modules) message(STATUS "Additional modules enabled due to dependencies: ${added_modules}") endif() - + # Update the global property with the full list of modules to build set_property(GLOBAL PROPERTY ATOM_ENABLED_MODULES "${enabled_modules}") message(STATUS "Final list of enabled modules: ${enabled_modules}") - + # Create a property to hold all module targets set_property(GLOBAL PROPERTY ATOM_MODULE_TARGETS "") - + # Enable the build for each required module foreach(module ${enabled_modules}) # Convert module name to CMake variable format string(REPLACE "atom-" "" module_name "${module}") string(TOUPPER "${module_name}" module_upper) - + # Set the corresponding build option to ON set(ATOM_BUILD_${module_upper} ON CACHE BOOL "Build ${module} module" FORCE) endforeach() diff --git a/cmake/TestsBuildOptions.cmake b/cmake/TestsBuildOptions.cmake index 4880990b..9b89a780 100644 --- a/cmake/TestsBuildOptions.cmake +++ b/cmake/TestsBuildOptions.cmake @@ -1,7 +1,7 @@ # TestsBuildOptions.cmake # # This file contains all options for controlling the build of Atom tests -# +# # Author: Max Qian # License: GPL3 diff --git a/cmake/VcpkgSetup.cmake b/cmake/VcpkgSetup.cmake index cf5e1107..2b4b452e 100644 --- a/cmake/VcpkgSetup.cmake +++ b/cmake/VcpkgSetup.cmake @@ -85,4 +85,4 @@ elseif(DEFINED ENV{VCPKG_ROOT} AND EXISTS "$ENV{VCPKG_ROOT}") else() message(FATAL_ERROR "Vcpkg root directory (ATOM_VCPKG_ROOT) could not be determined. " "Ensure VCPKG_ROOT is set or vcpkg is in a standard location, or CMAKE_TOOLCHAIN_FILE points to vcpkg.") -endif() \ No newline at end of file +endif() diff --git a/cmake/VersionConfig.cmake b/cmake/VersionConfig.cmake index dac3e09e..5cb71fe6 100644 --- a/cmake/VersionConfig.cmake +++ b/cmake/VersionConfig.cmake @@ -36,4 +36,4 @@ else() message(WARNING "cmake/version_info.h.in not found. Skipping generation of atom_version_info.h.") endif() -message(STATUS "Atom project version configured to: ${PROJECT_VERSION}") \ No newline at end of file +message(STATUS "Atom project version configured to: ${PROJECT_VERSION}") diff --git a/cmake/WSLDetection.cmake b/cmake/WSLDetection.cmake index 2af8f9f4..e802ca16 100644 --- a/cmake/WSLDetection.cmake +++ b/cmake/WSLDetection.cmake @@ -44,7 +44,7 @@ function(detect_wsl RESULT_VAR) set(${RESULT_VAR} TRUE PARENT_SCOPE) return() endif() - + # Default to not WSL set(${RESULT_VAR} FALSE PARENT_SCOPE) -endfunction() \ No newline at end of file +endfunction() diff --git a/cmake/compiler_options.cmake b/cmake/compiler_options.cmake index 2a2c5c14..e2437bc0 100644 --- a/cmake/compiler_options.cmake +++ b/cmake/compiler_options.cmake @@ -24,30 +24,30 @@ function(check_compiler_requirements) set(options "") set(oneValueArgs CXX_STANDARD MIN_GCC_VERSION MIN_CLANG_VERSION MIN_MSVC_VERSION) set(multiValueArgs "") - + cmake_parse_arguments(CHECK "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - + # Set default values if(NOT DEFINED CHECK_CXX_STANDARD) set(CHECK_CXX_STANDARD 23) endif() - + if(NOT DEFINED CHECK_MIN_GCC_VERSION) set(CHECK_MIN_GCC_VERSION 10.0) endif() - + if(NOT DEFINED CHECK_MIN_CLANG_VERSION) set(CHECK_MIN_CLANG_VERSION 10.0) endif() - + if(NOT DEFINED CHECK_MIN_MSVC_VERSION) set(CHECK_MIN_MSVC_VERSION 19.28) endif() - + # Check C++ standard support check_cxx_compiler_flag(-std=c++20 HAS_CXX20_FLAG) check_cxx_compiler_flag(-std=c++23 HAS_CXX23_FLAG) - + if(CHECK_CXX_STANDARD EQUAL 23) if(NOT HAS_CXX23_FLAG) message(FATAL_ERROR "C++23 standard support is required!") @@ -61,11 +61,11 @@ function(check_compiler_requirements) else() set(CMAKE_CXX_STANDARD ${CHECK_CXX_STANDARD} PARENT_SCOPE) endif() - + set(CMAKE_CXX_STANDARD_REQUIRED ON PARENT_SCOPE) set(CMAKE_CXX_EXTENSIONS OFF PARENT_SCOPE) set(CMAKE_C_STANDARD 17 PARENT_SCOPE) - + # Check compiler version if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") execute_process( @@ -95,7 +95,7 @@ function(check_compiler_requirements) message(STATUS "Using MSVC version ${CMAKE_CXX_COMPILER_VERSION}") endif() message(STATUS "Using C++${CMAKE_CXX_STANDARD}") - + # Set special flags for Apple platforms if(APPLE) check_cxx_compiler_flag(-stdlib=libc++ HAS_LIBCXX_FLAG) @@ -103,7 +103,7 @@ function(check_compiler_requirements) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++" PARENT_SCOPE) endif() endif() - + # Set build architecture for non-Apple platforms if(NOT APPLE) set(CMAKE_OSX_ARCHITECTURES x86_64 CACHE STRING "Build architecture for non-Apple platforms" FORCE) @@ -113,22 +113,22 @@ endfunction() # Configure compiler options function function(configure_compiler_options) # Parse arguments - set(options - ENABLE_WARNINGS TREAT_WARNINGS_AS_ERRORS + set(options + ENABLE_WARNINGS TREAT_WARNINGS_AS_ERRORS ENABLE_OPTIMIZATIONS ENABLE_DEBUG_INFO ENABLE_UTF8 ENABLE_EXCEPTION_HANDLING ENABLE_LTO ) set(oneValueArgs WARNING_LEVEL OPTIMIZATION_LEVEL) set(multiValueArgs ADDITIONAL_OPTIONS) - + cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - + # Set default values if(NOT DEFINED ARGS_WARNING_LEVEL) set(ARGS_WARNING_LEVEL "normal") endif() - + if(NOT DEFINED ARGS_OPTIMIZATION_LEVEL) if(CMAKE_BUILD_TYPE STREQUAL "Debug") set(ARGS_OPTIMIZATION_LEVEL "none") @@ -136,17 +136,17 @@ function(configure_compiler_options) set(ARGS_OPTIMIZATION_LEVEL "speed") endif() endif() - + set(compiler_options "") set(linker_options "") - + # MSVC compiler options if(MSVC) # Basic options list(APPEND compiler_options /nologo # Suppress copyright message ) - + # UTF-8 support if(ARGS_ENABLE_UTF8) list(APPEND compiler_options @@ -154,12 +154,12 @@ function(configure_compiler_options) /execution-charset:UTF-8 # Specify execution character set as UTF-8 ) endif() - + # Exception handling if(ARGS_ENABLE_EXCEPTION_HANDLING) list(APPEND compiler_options /EHsc) endif() - + # Warning level if(ARGS_ENABLE_WARNINGS) if(ARGS_WARNING_LEVEL STREQUAL "high") @@ -169,12 +169,12 @@ function(configure_compiler_options) else() list(APPEND compiler_options /W3) endif() - + if(ARGS_TREAT_WARNINGS_AS_ERRORS) list(APPEND compiler_options /WX) endif() endif() - + # Optimization level if(ARGS_ENABLE_OPTIMIZATIONS) if(ARGS_OPTIMIZATION_LEVEL STREQUAL "speed") @@ -187,30 +187,30 @@ function(configure_compiler_options) else() list(APPEND compiler_options /Od) endif() - + # Debug information if(ARGS_ENABLE_DEBUG_INFO) list(APPEND compiler_options /Zi) endif() - + # Link Time Optimization if(ARGS_ENABLE_LTO) list(APPEND compiler_options /GL) list(APPEND linker_options /LTCG) endif() - + # GCC/Clang compiler options elseif(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") # UTF-8 support if(ARGS_ENABLE_UTF8) list(APPEND compiler_options -fexec-charset=UTF-8) endif() - + # Exception handling if(ARGS_ENABLE_EXCEPTION_HANDLING) list(APPEND compiler_options -fexceptions) endif() - + # Warning level if(ARGS_ENABLE_WARNINGS) if(ARGS_WARNING_LEVEL STREQUAL "high") @@ -220,12 +220,12 @@ function(configure_compiler_options) else() list(APPEND compiler_options -Wall) endif() - + if(ARGS_TREAT_WARNINGS_AS_ERRORS) list(APPEND compiler_options -Werror) endif() endif() - + # Optimization level if(ARGS_ENABLE_OPTIMIZATIONS) if(ARGS_OPTIMIZATION_LEVEL STREQUAL "speed") @@ -238,34 +238,34 @@ function(configure_compiler_options) else() list(APPEND compiler_options -O0) endif() - + # Debug information if(ARGS_ENABLE_DEBUG_INFO) list(APPEND compiler_options -g) endif() - + # Link Time Optimization if(ARGS_ENABLE_LTO) list(APPEND compiler_options -flto) list(APPEND linker_options -flto) endif() endif() - + # Add user-provided additional options if(ARGS_ADDITIONAL_OPTIONS) list(APPEND compiler_options ${ARGS_ADDITIONAL_OPTIONS}) endif() - + # Apply compiler options add_compile_options(${compiler_options}) - + # Apply linker options if(linker_options) string(REPLACE ";" " " linker_flags_str "${linker_options}") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${linker_flags_str}" PARENT_SCOPE) set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${linker_flags_str}" PARENT_SCOPE) endif() - + # Print information message(STATUS "Configured compiler options: ${compiler_options}") if(linker_options) @@ -284,7 +284,7 @@ function(apply_build_preset PRESET_NAME) ENABLE_DEBUG_INFO ) add_definitions(-DDEBUG -D_DEBUG) - + elseif(PRESET_NAME STREQUAL "RELEASE") configure_compiler_options( ENABLE_UTF8 @@ -296,7 +296,7 @@ function(apply_build_preset PRESET_NAME) ENABLE_LTO ) add_definitions(-DNDEBUG) - + elseif(PRESET_NAME STREQUAL "MINSIZEREL") configure_compiler_options( ENABLE_UTF8 @@ -306,7 +306,7 @@ function(apply_build_preset PRESET_NAME) ENABLE_LTO ) add_definitions(-DNDEBUG) - + elseif(PRESET_NAME STREQUAL "RELWITHDEBINFO") configure_compiler_options( ENABLE_UTF8 @@ -315,7 +315,7 @@ function(apply_build_preset PRESET_NAME) ENABLE_DEBUG_INFO ) add_definitions(-DNDEBUG) - + elseif(PRESET_NAME STREQUAL "SANITIZE") # Enable code analysis and checking tools if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") @@ -346,7 +346,7 @@ function(configure_platform_options) elseif(UNIX AND NOT APPLE) add_definitions(-DPLATFORM_LINUX) endif() - + # Check architecture if(CMAKE_SIZEOF_VOID_P EQUAL 8) add_definitions(-DARCH_X64) @@ -361,9 +361,9 @@ macro(setup_project_defaults) set(options STATIC_RUNTIME ENABLE_PCH) set(oneValueArgs BUILD_PRESET CXX_STANDARD MIN_GCC_VERSION MIN_CLANG_VERSION MIN_MSVC_VERSION) set(multiValueArgs PCH_HEADERS) - + cmake_parse_arguments(SETUP "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - + # Check compiler requirements check_compiler_requirements( CXX_STANDARD ${SETUP_CXX_STANDARD} @@ -371,7 +371,7 @@ macro(setup_project_defaults) MIN_CLANG_VERSION ${SETUP_MIN_CLANG_VERSION} MIN_MSVC_VERSION ${SETUP_MIN_MSVC_VERSION} ) - + # Configure static runtime library if(SETUP_STATIC_RUNTIME AND MSVC) set(variables @@ -390,10 +390,10 @@ macro(setup_project_defaults) endif() endforeach() endif() - + # Apply platform options configure_platform_options() - + # Apply build preset if(DEFINED SETUP_BUILD_PRESET) apply_build_preset(${SETUP_BUILD_PRESET}) @@ -412,7 +412,7 @@ macro(setup_project_defaults) apply_build_preset("RELEASE") endif() endif() - + # Configure precompiled headers if(SETUP_ENABLE_PCH AND DEFINED SETUP_PCH_HEADERS) if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.16) @@ -426,4 +426,4 @@ macro(setup_project_defaults) message(WARNING "Precompiled header functionality requested, but CMake version does not support it (3.16+ required)") endif() endif() -endmacro() \ No newline at end of file +endmacro() diff --git a/cmake/module_dependencies.cmake b/cmake/module_dependencies.cmake index cb6e3d1e..37444cbe 100644 --- a/cmake/module_dependencies.cmake +++ b/cmake/module_dependencies.cmake @@ -66,4 +66,4 @@ set(ATOM_MODULE_BUILD_ORDER atom-search atom-secret atom-web -) \ No newline at end of file +) diff --git a/cmake/version_info.h.in b/cmake/version_info.h.in index 843d2998..c4a8b5a7 100644 --- a/cmake/version_info.h.in +++ b/cmake/version_info.h.in @@ -1,7 +1,7 @@ /** * @file atom_version_info.h * @brief Auto-generated Atom version information header file - * + * * This file is automatically generated by CMake, do not modify manually */ @@ -57,4 +57,4 @@ static inline int atom_check_version(int major, int minor, int patch) { } #endif -#endif /* ATOM_VERSION_INFO_H */ \ No newline at end of file +#endif /* ATOM_VERSION_INFO_H */ diff --git a/example/algorithm/CMakeLists.txt b/example/algorithm/CMakeLists.txt index d756f796..a650db84 100644 --- a/example/algorithm/CMakeLists.txt +++ b/example/algorithm/CMakeLists.txt @@ -14,20 +14,20 @@ file(GLOB CPP_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) foreach(CPP_FILE ${CPP_FILES}) # 获取不带扩展名的文件名 get_filename_component(EXAMPLE_NAME ${CPP_FILE} NAME_WE) - + # 构造可执行文件名称(子目录名_文件名) set(EXECUTABLE_NAME ${SUBDIR_NAME}_${EXAMPLE_NAME}) - + # 配置选项,允许单独控制每个示例的构建 string(TOUPPER ${EXAMPLE_NAME} EXAMPLE_NAME_UPPER) option(ATOM_EXAMPLE_ALGORITHM_${EXAMPLE_NAME_UPPER} "Build algorithm example: ${EXAMPLE_NAME}" ${ATOM_EXAMPLE_ALGORITHM_BUILD_ALL}) - + # 有条件地添加可执行文件 if(ATOM_EXAMPLE_ALGORITHM_${EXAMPLE_NAME_UPPER}) add_executable(${EXECUTABLE_NAME} ${CPP_FILE}) target_link_libraries(${EXECUTABLE_NAME} atom) - - + + # 设置IDE文件夹分组 set_property(TARGET ${EXECUTABLE_NAME} PROPERTY FOLDER "Examples/Algorithm") endif() diff --git a/example/algorithm/algorithm.cpp b/example/algorithm/algorithm.cpp index 86678cee..ee088348 100644 --- a/example/algorithm/algorithm.cpp +++ b/example/algorithm/algorithm.cpp @@ -154,4 +154,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/algorithm/annealing.cpp b/example/algorithm/annealing.cpp index 3d70c13d..b88baf52 100644 --- a/example/algorithm/annealing.cpp +++ b/example/algorithm/annealing.cpp @@ -109,4 +109,4 @@ int main() { << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/algorithm/base.cpp b/example/algorithm/base.cpp index 3212baa8..c0ae5ccc 100644 --- a/example/algorithm/base.cpp +++ b/example/algorithm/base.cpp @@ -132,4 +132,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/algorithm/bignumber.cpp b/example/algorithm/bignumber.cpp index 934c6fac..f9af2b5e 100644 --- a/example/algorithm/bignumber.cpp +++ b/example/algorithm/bignumber.cpp @@ -203,4 +203,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/algorithm/convolve.cpp b/example/algorithm/convolve.cpp index 21bed8c5..3613dd61 100644 --- a/example/algorithm/convolve.cpp +++ b/example/algorithm/convolve.cpp @@ -358,4 +358,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/algorithm/error_calibration.cpp b/example/algorithm/error_calibration.cpp index 3cbb4171..dbb12f77 100644 --- a/example/algorithm/error_calibration.cpp +++ b/example/algorithm/error_calibration.cpp @@ -56,4 +56,4 @@ int main() { calibrator.crossValidation(measured, actual, 5); return 0; -} \ No newline at end of file +} diff --git a/example/algorithm/flood.cpp b/example/algorithm/flood.cpp index 8b3d4b7a..a06e24cc 100644 --- a/example/algorithm/flood.cpp +++ b/example/algorithm/flood.cpp @@ -318,4 +318,4 @@ int main() { std::cout << "- Performance comparison between algorithms" << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/algorithm/fnmatch.cpp b/example/algorithm/fnmatch.cpp index 351d982c..e998b524 100644 --- a/example/algorithm/fnmatch.cpp +++ b/example/algorithm/fnmatch.cpp @@ -68,4 +68,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/algorithm/fraction.cpp b/example/algorithm/fraction.cpp index 7be37035..1b1b098c 100644 --- a/example/algorithm/fraction.cpp +++ b/example/algorithm/fraction.cpp @@ -86,4 +86,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/algorithm/hash.cpp b/example/algorithm/hash.cpp index 93bb4268..550d2d1b 100644 --- a/example/algorithm/hash.cpp +++ b/example/algorithm/hash.cpp @@ -96,4 +96,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/algorithm/huffman.cpp b/example/algorithm/huffman.cpp index 6415f839..75ab021e 100644 --- a/example/algorithm/huffman.cpp +++ b/example/algorithm/huffman.cpp @@ -66,4 +66,4 @@ int main() { atom::algorithm::visualizeHuffmanTree(huffmanTreeRoot.get()); return 0; -} \ No newline at end of file +} diff --git a/example/algorithm/math.cpp b/example/algorithm/math.cpp index ebc86f51..794b0964 100644 --- a/example/algorithm/math.cpp +++ b/example/algorithm/math.cpp @@ -123,4 +123,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/algorithm/matrix.cpp b/example/algorithm/matrix.cpp index 8ec60471..cf25accd 100644 --- a/example/algorithm/matrix.cpp +++ b/example/algorithm/matrix.cpp @@ -106,4 +106,4 @@ int main() { randomMatrix.print(); return 0; -} \ No newline at end of file +} diff --git a/example/algorithm/matrix_compress.cpp b/example/algorithm/matrix_compress.cpp index ef8e0792..a232ca98 100644 --- a/example/algorithm/matrix_compress.cpp +++ b/example/algorithm/matrix_compress.cpp @@ -132,4 +132,4 @@ int main() { #endif return 0; -} \ No newline at end of file +} diff --git a/example/algorithm/md5.cpp b/example/algorithm/md5.cpp index c723a874..ac9440e6 100644 --- a/example/algorithm/md5.cpp +++ b/example/algorithm/md5.cpp @@ -14,4 +14,4 @@ int main() { std::cout << "MD5 Hash: " << hash << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/algorithm/mhash.cpp b/example/algorithm/mhash.cpp index 21c0eea1..47c66691 100644 --- a/example/algorithm/mhash.cpp +++ b/example/algorithm/mhash.cpp @@ -63,4 +63,4 @@ int main() { std::cout << std::dec << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/algorithm/perlin.cpp b/example/algorithm/perlin.cpp index 59e0a23b..4a942312 100644 --- a/example/algorithm/perlin.cpp +++ b/example/algorithm/perlin.cpp @@ -42,4 +42,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/algorithm/rust_numeric.cpp b/example/algorithm/rust_numeric.cpp index dd96c4a7..b56c3654 100644 --- a/example/algorithm/rust_numeric.cpp +++ b/example/algorithm/rust_numeric.cpp @@ -339,4 +339,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/algorithm/sha1.cpp b/example/algorithm/sha1.cpp index 1df4d806..7c57b059 100644 --- a/example/algorithm/sha1.cpp +++ b/example/algorithm/sha1.cpp @@ -43,4 +43,4 @@ int main() { << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/algorithm/snowflake.cpp b/example/algorithm/snowflake.cpp index 0e2af7d7..88da73ff 100644 --- a/example/algorithm/snowflake.cpp +++ b/example/algorithm/snowflake.cpp @@ -39,4 +39,4 @@ int main() { std::cout << "Current Datacenter ID: " << currentDatacenterId << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/algorithm/tea.cpp b/example/algorithm/tea.cpp index eb6bcea3..ba9ae10b 100644 --- a/example/algorithm/tea.cpp +++ b/example/algorithm/tea.cpp @@ -83,4 +83,4 @@ int main() { std::cout << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/algorithm/weight.cpp b/example/algorithm/weight.cpp index 7125a014..c9ed2a76 100644 --- a/example/algorithm/weight.cpp +++ b/example/algorithm/weight.cpp @@ -115,4 +115,4 @@ int main() { std::cout << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/async/CMakeLists.txt b/example/async/CMakeLists.txt index 2cc215e7..e4995c49 100644 --- a/example/async/CMakeLists.txt +++ b/example/async/CMakeLists.txt @@ -14,20 +14,20 @@ file(GLOB CPP_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) foreach(CPP_FILE ${CPP_FILES}) # 获取不带扩展名的文件名 get_filename_component(EXAMPLE_NAME ${CPP_FILE} NAME_WE) - + # 构造可执行文件名称(子目录名_文件名) set(EXECUTABLE_NAME ${SUBDIR_NAME}_${EXAMPLE_NAME}) - + # 配置选项,允许单独控制每个示例的构建 string(TOUPPER ${EXAMPLE_NAME} EXAMPLE_NAME_UPPER) option(ATOM_EXAMPLE_ASYNC_${EXAMPLE_NAME_UPPER} "Build async example: ${EXAMPLE_NAME}" ${ATOM_EXAMPLE_ASYNC_BUILD_ALL}) - + # 有条件地添加可执行文件 if(ATOM_EXAMPLE_ASYNC_${EXAMPLE_NAME_UPPER}) add_executable(${EXECUTABLE_NAME} ${CPP_FILE}) target_link_libraries(${EXECUTABLE_NAME} atom) - - + + # 设置IDE文件夹分组 set_property(TARGET ${EXECUTABLE_NAME} PROPERTY FOLDER "Examples/Async") endif() diff --git a/example/async/async.cpp b/example/async/async.cpp index 57619703..747f9120 100644 --- a/example/async/async.cpp +++ b/example/async/async.cpp @@ -438,4 +438,4 @@ int main(int argc, char* argv[]) { } return 0; -} \ No newline at end of file +} diff --git a/example/async/async_executor.cpp b/example/async/async_executor.cpp index 3be79b31..ebdba358 100644 --- a/example/async/async_executor.cpp +++ b/example/async/async_executor.cpp @@ -562,4 +562,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/async/daemon.cpp b/example/async/daemon.cpp index 745e8f7e..72a47f4b 100644 --- a/example/async/daemon.cpp +++ b/example/async/daemon.cpp @@ -16,16 +16,16 @@ namespace examples { int simpleTask(int argc, char** argv) { std::cout << "简单任务开始执行" << std::endl; std::cout << "参数数量: " << argc << std::endl; - + for (int i = 0; i < argc; ++i) { std::cout << "参数[" << i << "]: " << (argv[i] ? argv[i] : "nullptr") << std::endl; } - + // 模拟工作 std::cout << "任务正在执行..." << std::endl; std::this_thread::sleep_for(std::chrono::seconds(2)); std::cout << "简单任务执行完成" << std::endl; - + return 0; } @@ -48,17 +48,17 @@ namespace examples { int simpleTask(int argc, char** argv) { std::cout << "简单任务开始执行" << std::endl; std::cout << "参数数量: " << argc << std::endl; - + for (int i = 0; i < argc; ++i) { std::cout << "参数[" << i << "]: " << (argv[i] ? argv[i] : "nullptr") << std::endl; } - + // 模拟工作 std::cout << "任务正在执行..." << std::endl; std::this_thread::sleep_for(std::chrono::seconds(2)); std::cout << "简单任务执行完成" << std::endl; - + return 0; } -// 简单的任务回调函数 - 现代方式 (使用 std::span \ No newline at end of file +// 简单的任务回调函数 - 现代方式 (使用 std::span diff --git a/example/async/eventstack.cpp b/example/async/eventstack.cpp index 6e867e50..32f4e012 100644 --- a/example/async/eventstack.cpp +++ b/example/async/eventstack.cpp @@ -352,4 +352,4 @@ int main() { std::cout << "\n所有测试完成!\n"; return 0; -} \ No newline at end of file +} diff --git a/example/async/future.cpp b/example/async/future.cpp index 2d021a69..b087a73e 100644 --- a/example/async/future.cpp +++ b/example/async/future.cpp @@ -346,4 +346,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/async/generator.cpp b/example/async/generator.cpp index 8681b421..85b64255 100644 --- a/example/async/generator.cpp +++ b/example/async/generator.cpp @@ -494,4 +494,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/async/limiter.cpp b/example/async/limiter.cpp index d9af1da9..f9f05469 100644 --- a/example/async/limiter.cpp +++ b/example/async/limiter.cpp @@ -613,4 +613,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/async/lock.cpp b/example/async/lock.cpp index f7978a9a..e494de56 100644 --- a/example/async/lock.cpp +++ b/example/async/lock.cpp @@ -603,4 +603,4 @@ int main() { std::cout << "\n========= 示例完成 =========\n"; return 0; -} \ No newline at end of file +} diff --git a/example/async/message_bus.cpp b/example/async/message_bus.cpp index 499f62f3..e09fe795 100644 --- a/example/async/message_bus.cpp +++ b/example/async/message_bus.cpp @@ -61,4 +61,4 @@ int main() { ioThread.join(); return 0; -} \ No newline at end of file +} diff --git a/example/async/message_queue.cpp b/example/async/message_queue.cpp index d770fea3..1be823fa 100644 --- a/example/async/message_queue.cpp +++ b/example/async/message_queue.cpp @@ -71,4 +71,4 @@ int main() { processingThread.join(); return 0; -} \ No newline at end of file +} diff --git a/example/async/packaged_task.cpp b/example/async/packaged_task.cpp index 8c6204cb..2a58867e 100644 --- a/example/async/packaged_task.cpp +++ b/example/async/packaged_task.cpp @@ -744,4 +744,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/async/parallel.cpp b/example/async/parallel.cpp index 4ed811cc..b6cdddc9 100644 --- a/example/async/parallel.cpp +++ b/example/async/parallel.cpp @@ -386,7 +386,7 @@ void cpp20_features_example() { { Timer t("使用 span 进行映射操作"); std::span data_view(data); - + // 创建结果向量 std::vector results(data_view.size()); for (size_t i = 0; i < data_view.size(); ++i) { @@ -406,10 +406,10 @@ void cpp20_features_example() { struct Person { std::string name; int age; - + // 移除局部类中的友元函数定义 }; - + // 定义非成员操作符重载 std::ostream& operator<<(std::ostream& os, const Person& p) { return os << p.name << "(" << p.age << ")"; @@ -422,14 +422,14 @@ void cpp20_features_example() { // 使用标准库过滤数据 { Timer t("使用标准库过滤成年人"); - + std::vector adults; for (const auto& p : people) { if (p.age >= 18) { adults.push_back(p); } } - + // 注释掉有问题的调用 // auto adults = atom::async::Parallel::filter_range( // people, [](const Person& p) { return p.age >= 18; }); @@ -441,9 +441,9 @@ void cpp20_features_example() { // 7. 协程任务示例 - 使用标准库替代 void coroutine_task_example() { std::cout << "\n===== 协程任务示例 =====\n"; - + std::cout << "注意:协程示例需要使用 atom::async::Task,已被注释" << std::endl; - + // 简化协程示例,使用标准线程代替 auto simple_task = []() -> int { std::cout << "执行简单任务..." << std::endl; @@ -522,7 +522,7 @@ void simd_operations_example() { for (size_t i = 0; i < size; ++i) { result[i] = a[i] + b[i]; } - + // 注释掉有问题的调用 // atom::async::SimdOps::add(a.data(), b.data(), result.data(), size); @@ -553,7 +553,7 @@ void simd_operations_example() { for (size_t i = 0; i < size; ++i) { result[i] = a[i] * b[i]; } - + // 注释掉有问题的调用 // atom::async::SimdOps::multiply(a.data(), b.data(), result.data(), size); @@ -585,7 +585,7 @@ void simd_operations_example() { for (size_t i = 0; i < size; ++i) { dot_result += a[i] * b[i]; } - + // 注释掉有问题的调用 // float dot_result = atom::async::SimdOps::dotProduct(a.data(), b.data(), size); @@ -622,7 +622,7 @@ void simd_operations_example() { for (size_t i = 0; i < span_a.size(); ++i) { dot_result += span_a[i] * span_b[i]; } - + // 注释掉有问题的调用 // float dot_result = atom::async::SimdOps::dotProduct(span_a, span_b); std::cout << "使用 span 的点积结果: " << dot_result @@ -647,11 +647,11 @@ void edge_cases_and_error_handling() { try { // 使用标准库代替 std::for_each(empty_data.begin(), empty_data.end(), [](int& x) { x *= 2; }); - + // 注释掉有问题的调用 // atom::async::Parallel::for_each( // empty_data.begin(), empty_data.end(), [](int& x) { x *= 2; }); - + std::cout << "空数据集的 for_each 成功完成" << std::endl; } catch (const std::exception& e) { std::cout << "空数据集的 for_each 发生错误: " << e.what() @@ -666,12 +666,12 @@ void edge_cases_and_error_handling() { for (int x : empty_data) { result.push_back(x * 2); } - + // 注释掉有问题的调用 // auto result = // atom::async::Parallel::map(empty_data.begin(), empty_data.end(), // [](int x) { return x * 2; }); - + std::cout << "空数据集的 map 成功完成,结果大小: " << result.size() << std::endl; } catch (const std::exception& e) { @@ -688,12 +688,12 @@ void edge_cases_and_error_handling() { try { // 使用标准库代替 int result = std::accumulate(single_data.begin(), single_data.end(), 10); - + // 注释掉有问题的调用 // int result = atom::async::Parallel::reduce( // single_data.begin(), single_data.end(), 10, // [](int a, int b) { return a + b; }); - + std::cout << "单元素数据集的 reduce 结果: " << result << std::endl; } catch (const std::exception& e) { std::cout << "单元素数据集的 reduce 发生错误: " << e.what() @@ -704,10 +704,10 @@ void edge_cases_and_error_handling() { try { // 使用标准库代替 std::sort(single_data.begin(), single_data.end()); - + // 注释掉有问题的调用 // atom::async::Parallel::sort(single_data.begin(), single_data.end()); - + std::cout << "单元素数据集的 sort 成功完成,结果: " << single_data[0] << std::endl; } catch (const std::exception& e) { @@ -730,12 +730,12 @@ void edge_cases_and_error_handling() { if (b.data() == nullptr || result.data() == nullptr) { throw std::invalid_argument("输入指针不能为空"); } - + // 模拟正常操作 for (size_t i = 0; i < 2; ++i) { result[i] = 0 + b[i]; // 模拟 a 为空 } - + // 注释掉有问题的调用 // atom::async::SimdOps::add(nullptr, b.data(), result.data(), 2); std::cout << "不应该看到这行输出!" << std::endl; @@ -753,7 +753,7 @@ void edge_cases_and_error_handling() { if (span_a.size() != span_c.size()) { throw std::invalid_argument("向量大小不匹配"); } - + // 注释掉有问题的调用 // float result = atom::async::SimdOps::dotProduct(span_a, span_c); std::cout << "不应该看到这行输出!" << std::endl; @@ -798,13 +798,13 @@ void jthread_example() { // 使用标准库代替 jthread 实现的 for_each { Timer t("使用 std::for_each 的处理"); - + // 使用标准库代替 std::for_each(data.begin(), data.end(), [&counter](int& val) { val *= 2; // 乘以2 counter++; }); - + // 注释掉有问题的调用 // atom::async::Parallel::for_each_jthread(data.begin(), data.end(), // [&counter](int& val) { @@ -823,13 +823,13 @@ void jthread_example() { // 使用标准库代替 jthread 实现的 for_each { Timer t("使用 std::for_each 的处理 (模拟4个线程)"); - + // 使用标准库代替 std::for_each(data.begin(), data.end(), [&counter](int& val) { val *= 2; // 乘以2 counter++; }); - + // 注释掉有问题的调用 // atom::async::Parallel::for_each_jthread( // data.begin(), data.end(), @@ -862,4 +862,4 @@ int main() { std::cout << "\n========== 示例完成 ==========\n"; return 0; -} \ No newline at end of file +} diff --git a/example/async/pool.cpp b/example/async/pool.cpp index b432f83b..1f1475da 100644 --- a/example/async/pool.cpp +++ b/example/async/pool.cpp @@ -476,4 +476,4 @@ int main() { std::cout << "\n所有示例已完成\n" << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/async/promise.cpp b/example/async/promise.cpp index 88dea53d..6b04c094 100644 --- a/example/async/promise.cpp +++ b/example/async/promise.cpp @@ -728,4 +728,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/async/queue.cpp b/example/async/queue.cpp index 2b681674..a5398e2b 100644 --- a/example/async/queue.cpp +++ b/example/async/queue.cpp @@ -630,4 +630,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/async/safetype.cpp b/example/async/safetype.cpp index da3411ef..3ee5b08e 100644 --- a/example/async/safetype.cpp +++ b/example/async/safetype.cpp @@ -616,4 +616,4 @@ int main() { std::cout << "\n所有示例已成功完成!" << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/async/slot.cpp b/example/async/slot.cpp index a017faf9..0823dce1 100644 --- a/example/async/slot.cpp +++ b/example/async/slot.cpp @@ -389,4 +389,4 @@ int main() { std::cout << "\n所有示例已完成!" << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/async/thread_wrapper.cpp b/example/async/thread_wrapper.cpp index 1b53244b..fd05b0a5 100644 --- a/example/async/thread_wrapper.cpp +++ b/example/async/thread_wrapper.cpp @@ -620,4 +620,4 @@ int main() { print_safe("\nAll examples completed"); return 0; -} \ No newline at end of file +} diff --git a/example/async/threadlocal.cpp b/example/async/threadlocal.cpp index 901e587f..3aa8534c 100644 --- a/example/async/threadlocal.cpp +++ b/example/async/threadlocal.cpp @@ -526,4 +526,4 @@ int main() { std::cout << "\n===== 示例完成 =====\n"; return 0; -} \ No newline at end of file +} diff --git a/example/async/timer.cpp b/example/async/timer.cpp index eb5e2c1d..7c772a5b 100644 --- a/example/async/timer.cpp +++ b/example/async/timer.cpp @@ -484,4 +484,4 @@ int main() { std::cout << "\n===== 示例完成 =====\n"; return 0; -} \ No newline at end of file +} diff --git a/example/async/trigger.cpp b/example/async/trigger.cpp index d4301a5d..b5b5e4b2 100644 --- a/example/async/trigger.cpp +++ b/example/async/trigger.cpp @@ -489,4 +489,4 @@ int main() { std::cout << "\n===== Examples Complete =====\n"; return 0; -} \ No newline at end of file +} diff --git a/example/components/CMakeLists.txt b/example/components/CMakeLists.txt index 13ce9567..f6faa642 100644 --- a/example/components/CMakeLists.txt +++ b/example/components/CMakeLists.txt @@ -14,20 +14,20 @@ file(GLOB CPP_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) foreach(CPP_FILE ${CPP_FILES}) # 获取不带扩展名的文件名 get_filename_component(EXAMPLE_NAME ${CPP_FILE} NAME_WE) - + # 构造可执行文件名称(子目录名_文件名) set(EXECUTABLE_NAME ${SUBDIR_NAME}_${EXAMPLE_NAME}) - + # 配置选项,允许单独控制每个示例的构建 string(TOUPPER ${EXAMPLE_NAME} EXAMPLE_NAME_UPPER) option(ATOM_EXAMPLE_COMPONENTS_${EXAMPLE_NAME_UPPER} "Build components example: ${EXAMPLE_NAME}" ${ATOM_EXAMPLE_COMPONENTS_BUILD_ALL}) - + # 有条件地添加可执行文件 if(ATOM_EXAMPLE_COMPONENTS_${EXAMPLE_NAME_UPPER}) add_executable(${EXECUTABLE_NAME} ${CPP_FILE}) target_link_libraries(${EXECUTABLE_NAME} atom) - - + + # 设置IDE文件夹分组 set_property(TARGET ${EXECUTABLE_NAME} PROPERTY FOLDER "Examples/Components") endif() diff --git a/example/connection/CMakeLists.txt b/example/connection/CMakeLists.txt index bd20d10c..cfc5e631 100644 --- a/example/connection/CMakeLists.txt +++ b/example/connection/CMakeLists.txt @@ -14,20 +14,20 @@ file(GLOB CPP_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) foreach(CPP_FILE ${CPP_FILES}) # 获取不带扩展名的文件名 get_filename_component(EXAMPLE_NAME ${CPP_FILE} NAME_WE) - + # 构造可执行文件名称(子目录名_文件名) set(EXECUTABLE_NAME ${SUBDIR_NAME}_${EXAMPLE_NAME}) - + # 配置选项,允许单独控制每个示例的构建 string(TOUPPER ${EXAMPLE_NAME} EXAMPLE_NAME_UPPER) option(ATOM_EXAMPLE_CONNECTION_${EXAMPLE_NAME_UPPER} "Build connection example: ${EXAMPLE_NAME}" ${ATOM_EXAMPLE_CONNECTION_BUILD_ALL}) - + # 有条件地添加可执行文件 if(ATOM_EXAMPLE_CONNECTION_${EXAMPLE_NAME_UPPER}) add_executable(${EXECUTABLE_NAME} ${CPP_FILE}) target_link_libraries(${EXECUTABLE_NAME} atom) - - + + # 设置IDE文件夹分组 set_property(TARGET ${EXECUTABLE_NAME} PROPERTY FOLDER "Examples/Connection") endif() diff --git a/example/connection/async_fifoclient.cpp b/example/connection/async_fifoclient.cpp index 627d29b0..f7dc9d95 100644 --- a/example/connection/async_fifoclient.cpp +++ b/example/connection/async_fifoclient.cpp @@ -134,4 +134,4 @@ int main() { std::cout << "\nFifoClient example completed successfully" << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/connection/async_fifoserver.cpp b/example/connection/async_fifoserver.cpp index bbfffe60..c8912387 100644 --- a/example/connection/async_fifoserver.cpp +++ b/example/connection/async_fifoserver.cpp @@ -163,4 +163,4 @@ int main() { std::cerr << "Fatal error: " << e.what() << std::endl; return 1; } -} \ No newline at end of file +} diff --git a/example/connection/async_sockethub.cpp b/example/connection/async_sockethub.cpp index 5379411d..c38d29e3 100644 --- a/example/connection/async_sockethub.cpp +++ b/example/connection/async_sockethub.cpp @@ -295,4 +295,4 @@ int main() { Logger::log("Main", std::string("Fatal error: ") + e.what()); return 1; } -} \ No newline at end of file +} diff --git a/example/connection/async_tcpclient.cpp b/example/connection/async_tcpclient.cpp index aed34da1..1b0ba2f4 100644 --- a/example/connection/async_tcpclient.cpp +++ b/example/connection/async_tcpclient.cpp @@ -399,4 +399,4 @@ int main() { std::string("Fatal error: ") + e.what()); return 1; } -} \ No newline at end of file +} diff --git a/example/connection/async_udpclient.cpp b/example/connection/async_udpclient.cpp index 9d04dfa1..090c5a1b 100644 --- a/example/connection/async_udpclient.cpp +++ b/example/connection/async_udpclient.cpp @@ -69,4 +69,4 @@ int main() { std::cout << "Stopped receiving data" << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/connection/async_udpserver.cpp b/example/connection/async_udpserver.cpp index dd849580..37699546 100644 --- a/example/connection/async_udpserver.cpp +++ b/example/connection/async_udpserver.cpp @@ -50,4 +50,4 @@ int main() { std::cout << "Server stopped" << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/error/CMakeLists.txt b/example/error/CMakeLists.txt index e17057a3..06a85992 100644 --- a/example/error/CMakeLists.txt +++ b/example/error/CMakeLists.txt @@ -14,20 +14,20 @@ file(GLOB CPP_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) foreach(CPP_FILE ${CPP_FILES}) # 获取不带扩展名的文件名 get_filename_component(EXAMPLE_NAME ${CPP_FILE} NAME_WE) - + # 构造可执行文件名称(子目录名_文件名) set(EXECUTABLE_NAME ${SUBDIR_NAME}_${EXAMPLE_NAME}) - + # 配置选项,允许单独控制每个示例的构建 string(TOUPPER ${EXAMPLE_NAME} EXAMPLE_NAME_UPPER) option(ATOM_EXAMPLE_ERROR_${EXAMPLE_NAME_UPPER} "Build error example: ${EXAMPLE_NAME}" ${ATOM_EXAMPLE_ERROR_BUILD_ALL}) - + # 有条件地添加可执行文件 if(ATOM_EXAMPLE_ERROR_${EXAMPLE_NAME_UPPER}) add_executable(${EXECUTABLE_NAME} ${CPP_FILE}) target_link_libraries(${EXECUTABLE_NAME} atom) - - + + # 设置IDE文件夹分组 set_property(TARGET ${EXECUTABLE_NAME} PROPERTY FOLDER "Examples/Error") endif() diff --git a/example/error/exception.cpp b/example/error/exception.cpp index 197fd5e2..2fe23f28 100644 --- a/example/error/exception.cpp +++ b/example/error/exception.cpp @@ -312,4 +312,4 @@ void testException() { int main() { testException(); return 0; -} \ No newline at end of file +} diff --git a/example/error/stacktrace.cpp b/example/error/stacktrace.cpp index d1ca59e3..a4e61044 100644 --- a/example/error/stacktrace.cpp +++ b/example/error/stacktrace.cpp @@ -28,4 +28,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/extra/CMakeLists.txt b/example/extra/CMakeLists.txt index 014dcfa8..04247bd9 100644 --- a/example/extra/CMakeLists.txt +++ b/example/extra/CMakeLists.txt @@ -14,20 +14,20 @@ file(GLOB CPP_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) foreach(CPP_FILE ${CPP_FILES}) # 获取不带扩展名的文件名 get_filename_component(EXAMPLE_NAME ${CPP_FILE} NAME_WE) - + # 构造可执行文件名称(子目录名_文件名) set(EXECUTABLE_NAME ${SUBDIR_NAME}_${EXAMPLE_NAME}) - + # 配置选项,允许单独控制每个示例的构建 string(TOUPPER ${EXAMPLE_NAME} EXAMPLE_NAME_UPPER) option(ATOM_EXAMPLE_EXTRA_${EXAMPLE_NAME_UPPER} "Build extra example: ${EXAMPLE_NAME}" ${ATOM_EXAMPLE_EXTRA_BUILD_ALL}) - + # 有条件地添加可执行文件 if(ATOM_EXAMPLE_EXTRA_${EXAMPLE_NAME_UPPER}) add_executable(${EXECUTABLE_NAME} ${CPP_FILE}) target_link_libraries(${EXECUTABLE_NAME} atom) - - + + # 设置IDE文件夹分组 set_property(TARGET ${EXECUTABLE_NAME} PROPERTY FOLDER "Examples/Extra") endif() diff --git a/example/extra/beast/http.cpp b/example/extra/beast/http.cpp index 5c5b6ab9..afe3bbe2 100644 --- a/example/extra/beast/http.cpp +++ b/example/extra/beast/http.cpp @@ -140,4 +140,4 @@ int main() { ioc.run(); return 0; -} \ No newline at end of file +} diff --git a/example/extra/beast/ws.cpp b/example/extra/beast/ws.cpp index bf7b7fdf..b70e447d 100644 --- a/example/extra/beast/ws.cpp +++ b/example/extra/beast/ws.cpp @@ -114,4 +114,4 @@ int main() { ioc.run(); return 0; -} \ No newline at end of file +} diff --git a/example/extra/boost/charconv.cpp b/example/extra/boost/charconv.cpp index 1e5c2ce8..cc412a5d 100644 --- a/example/extra/boost/charconv.cpp +++ b/example/extra/boost/charconv.cpp @@ -94,4 +94,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/extra/boost/locale.cpp b/example/extra/boost/locale.cpp index 08e1823b..ee3c1990 100644 --- a/example/extra/boost/locale.cpp +++ b/example/extra/boost/locale.cpp @@ -87,4 +87,4 @@ int main() { std::cout << "Formatted string: " << formattedStr << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/extra/boost/math.cpp b/example/extra/boost/math.cpp index 3aab81ee..40319d48 100644 --- a/example/extra/boost/math.cpp +++ b/example/extra/boost/math.cpp @@ -157,4 +157,4 @@ int main() { << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/extra/boost/regex.cpp b/example/extra/boost/regex.cpp index b2a246ec..67023eec 100644 --- a/example/extra/boost/regex.cpp +++ b/example/extra/boost/regex.cpp @@ -112,4 +112,4 @@ int main() { << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/extra/boost/system.cpp b/example/extra/boost/system.cpp index ed428992..e8ef9911 100644 --- a/example/extra/boost/system.cpp +++ b/example/extra/boost/system.cpp @@ -103,4 +103,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/extra/boost/uuid.cpp b/example/extra/boost/uuid.cpp index 33f61f50..f712081c 100644 --- a/example/extra/boost/uuid.cpp +++ b/example/extra/boost/uuid.cpp @@ -115,4 +115,4 @@ int main() { std::cout << "UUID hash value: " << hashValue << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/extra/uv/subprocess.cpp b/example/extra/uv/subprocess.cpp index d2e20ef6..f27e1d58 100644 --- a/example/extra/uv/subprocess.cpp +++ b/example/extra/uv/subprocess.cpp @@ -450,4 +450,4 @@ int main() { std::cerr << "\n\nSome examples failed!" << std::endl; return 1; } -} \ No newline at end of file +} diff --git a/example/image/CMakeLists.txt b/example/image/CMakeLists.txt index 098cd118..d0089f2d 100644 --- a/example/image/CMakeLists.txt +++ b/example/image/CMakeLists.txt @@ -14,20 +14,20 @@ file(GLOB CPP_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) foreach(CPP_FILE ${CPP_FILES}) # 获取不带扩展名的文件名 get_filename_component(EXAMPLE_NAME ${CPP_FILE} NAME_WE) - + # 构造可执行文件名称(子目录名_文件名) set(EXECUTABLE_NAME ${SUBDIR_NAME}_${EXAMPLE_NAME}) - + # 配置选项,允许单独控制每个示例的构建 string(TOUPPER ${EXAMPLE_NAME} EXAMPLE_NAME_UPPER) option(ATOM_EXAMPLE_IMAGE_${EXAMPLE_NAME_UPPER} "Build image example: ${EXAMPLE_NAME}" ${ATOM_EXAMPLE_IMAGE_BUILD_ALL}) - + # 有条件地添加可执行文件 if(ATOM_EXAMPLE_IMAGE_${EXAMPLE_NAME_UPPER}) add_executable(${EXECUTABLE_NAME} ${CPP_FILE}) target_link_libraries(${EXECUTABLE_NAME} atom) - - + + # 设置IDE文件夹分组 set_property(TARGET ${EXECUTABLE_NAME} PROPERTY FOLDER "Examples/Image") endif() diff --git a/example/io/CMakeLists.txt b/example/io/CMakeLists.txt index 8866c8a6..db4b9192 100644 --- a/example/io/CMakeLists.txt +++ b/example/io/CMakeLists.txt @@ -14,20 +14,20 @@ file(GLOB CPP_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) foreach(CPP_FILE ${CPP_FILES}) # 获取不带扩展名的文件名 get_filename_component(EXAMPLE_NAME ${CPP_FILE} NAME_WE) - + # 构造可执行文件名称(子目录名_文件名) set(EXECUTABLE_NAME ${SUBDIR_NAME}_${EXAMPLE_NAME}) - + # 配置选项,允许单独控制每个示例的构建 string(TOUPPER ${EXAMPLE_NAME} EXAMPLE_NAME_UPPER) option(ATOM_EXAMPLE_IO_${EXAMPLE_NAME_UPPER} "Build io example: ${EXAMPLE_NAME}" ${ATOM_EXAMPLE_IO_BUILD_ALL}) - + # 有条件地添加可执行文件 if(ATOM_EXAMPLE_IO_${EXAMPLE_NAME_UPPER}) add_executable(${EXECUTABLE_NAME} ${CPP_FILE}) target_link_libraries(${EXECUTABLE_NAME} atom) - - + + # 设置IDE文件夹分组 set_property(TARGET ${EXECUTABLE_NAME} PROPERTY FOLDER "Examples/Io") endif() diff --git a/example/log/CMakeLists.txt b/example/log/CMakeLists.txt index b4bb314b..add90c10 100644 --- a/example/log/CMakeLists.txt +++ b/example/log/CMakeLists.txt @@ -14,20 +14,20 @@ file(GLOB CPP_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) foreach(CPP_FILE ${CPP_FILES}) # 获取不带扩展名的文件名 get_filename_component(EXAMPLE_NAME ${CPP_FILE} NAME_WE) - + # 构造可执行文件名称(子目录名_文件名) set(EXECUTABLE_NAME ${SUBDIR_NAME}_${EXAMPLE_NAME}) - + # 配置选项,允许单独控制每个示例的构建 string(TOUPPER ${EXAMPLE_NAME} EXAMPLE_NAME_UPPER) option(ATOM_EXAMPLE_LOG_${EXAMPLE_NAME_UPPER} "Build log example: ${EXAMPLE_NAME}" ${ATOM_EXAMPLE_LOG_BUILD_ALL}) - + # 有条件地添加可执行文件 if(ATOM_EXAMPLE_LOG_${EXAMPLE_NAME_UPPER}) add_executable(${EXECUTABLE_NAME} ${CPP_FILE}) target_link_libraries(${EXECUTABLE_NAME} atom) - - + + # 设置IDE文件夹分组 set_property(TARGET ${EXECUTABLE_NAME} PROPERTY FOLDER "Examples/Log") endif() diff --git a/example/log/async_logger.cpp b/example/log/async_logger.cpp index 1fb00554..f64e3d62 100644 --- a/example/log/async_logger.cpp +++ b/example/log/async_logger.cpp @@ -211,4 +211,4 @@ int main() { std::cout << "\nAll examples completed.\nCheck the logs/ directory for " "output files.\n"; return 0; -} \ No newline at end of file +} diff --git a/example/log/atomlog.cpp b/example/log/atomlog.cpp index d46e5a2f..d023da76 100644 --- a/example/log/atomlog.cpp +++ b/example/log/atomlog.cpp @@ -44,4 +44,4 @@ int main() { logger.clearSinks(); return 0; -} \ No newline at end of file +} diff --git a/example/log/logger.cpp b/example/log/logger.cpp index f4f19f99..38b2dfda 100644 --- a/example/log/logger.cpp +++ b/example/log/logger.cpp @@ -36,4 +36,4 @@ int main() { loggerManager.analyzeLogs(); return 0; -} \ No newline at end of file +} diff --git a/example/memory/CMakeLists.txt b/example/memory/CMakeLists.txt index 0b4981a1..dbc13194 100644 --- a/example/memory/CMakeLists.txt +++ b/example/memory/CMakeLists.txt @@ -14,20 +14,20 @@ file(GLOB CPP_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) foreach(CPP_FILE ${CPP_FILES}) # 获取不带扩展名的文件名 get_filename_component(EXAMPLE_NAME ${CPP_FILE} NAME_WE) - + # 构造可执行文件名称(子目录名_文件名) set(EXECUTABLE_NAME ${SUBDIR_NAME}_${EXAMPLE_NAME}) - + # 配置选项,允许单独控制每个示例的构建 string(TOUPPER ${EXAMPLE_NAME} EXAMPLE_NAME_UPPER) option(ATOM_EXAMPLE_MEMORY_${EXAMPLE_NAME_UPPER} "Build memory example: ${EXAMPLE_NAME}" ${ATOM_EXAMPLE_MEMORY_BUILD_ALL}) - + # 有条件地添加可执行文件 if(ATOM_EXAMPLE_MEMORY_${EXAMPLE_NAME_UPPER}) add_executable(${EXECUTABLE_NAME} ${CPP_FILE}) target_link_libraries(${EXECUTABLE_NAME} atom) - - + + # 设置IDE文件夹分组 set_property(TARGET ${EXECUTABLE_NAME} PROPERTY FOLDER "Examples/Memory") endif() diff --git a/example/memory/memory.cpp b/example/memory/memory.cpp index bde7ad6c..9d074e4d 100644 --- a/example/memory/memory.cpp +++ b/example/memory/memory.cpp @@ -595,4 +595,4 @@ int main() { std::cout << "\nAll Memory Pool examples completed successfully!\n"; return 0; -} \ No newline at end of file +} diff --git a/example/memory/object.cpp b/example/memory/object.cpp index 3b7d692d..f2d9f5d5 100644 --- a/example/memory/object.cpp +++ b/example/memory/object.cpp @@ -696,4 +696,4 @@ int main() { std::cout << "\nAll ObjectPool examples completed successfully!\n"; return 0; -} \ No newline at end of file +} diff --git a/example/memory/ring.cpp b/example/memory/ring.cpp index 0a92260c..830781be 100644 --- a/example/memory/ring.cpp +++ b/example/memory/ring.cpp @@ -674,4 +674,4 @@ int main() { std::cout << "\nAll RingBuffer examples completed successfully!\n"; return 0; -} \ No newline at end of file +} diff --git a/example/memory/shared.cpp b/example/memory/shared.cpp index 9ea5e0e3..76c7823a 100644 --- a/example/memory/shared.cpp +++ b/example/memory/shared.cpp @@ -907,4 +907,4 @@ int main() { // The destructors for the SharedMemory objects will handle resource cleanup return 0; -} \ No newline at end of file +} diff --git a/example/memory/short_alloc.cpp b/example/memory/short_alloc.cpp index 13ac75b8..a8338f51 100644 --- a/example/memory/short_alloc.cpp +++ b/example/memory/short_alloc.cpp @@ -1033,4 +1033,4 @@ int main() { << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/memory/tracker.cpp b/example/memory/tracker.cpp index b8cfb753..bb483e94 100644 --- a/example/memory/tracker.cpp +++ b/example/memory/tracker.cpp @@ -541,4 +541,4 @@ int main() { << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/memory/utils.cpp b/example/memory/utils.cpp index 44c7c4d5..59cad253 100644 --- a/example/memory/utils.cpp +++ b/example/memory/utils.cpp @@ -538,4 +538,4 @@ int main() { std::cout << " 8. Combined usage of multiple utilities" << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/meta/CMakeLists.txt b/example/meta/CMakeLists.txt index a280ac9f..2ef618d3 100644 --- a/example/meta/CMakeLists.txt +++ b/example/meta/CMakeLists.txt @@ -14,20 +14,20 @@ file(GLOB CPP_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) foreach(CPP_FILE ${CPP_FILES}) # 获取不带扩展名的文件名 get_filename_component(EXAMPLE_NAME ${CPP_FILE} NAME_WE) - + # 构造可执行文件名称(子目录名_文件名) set(EXECUTABLE_NAME ${SUBDIR_NAME}_${EXAMPLE_NAME}) - + # 配置选项,允许单独控制每个示例的构建 string(TOUPPER ${EXAMPLE_NAME} EXAMPLE_NAME_UPPER) option(ATOM_EXAMPLE_META_${EXAMPLE_NAME_UPPER} "Build meta example: ${EXAMPLE_NAME}" ${ATOM_EXAMPLE_META_BUILD_ALL}) - + # 有条件地添加可执行文件 if(ATOM_EXAMPLE_META_${EXAMPLE_NAME_UPPER}) add_executable(${EXECUTABLE_NAME} ${CPP_FILE}) target_link_libraries(${EXECUTABLE_NAME} atom) - - + + # 设置IDE文件夹分组 set_property(TARGET ${EXECUTABLE_NAME} PROPERTY FOLDER "Examples/Meta") endif() diff --git a/example/meta/abi.cpp b/example/meta/abi.cpp index 3efee2ad..29ff09a5 100644 --- a/example/meta/abi.cpp +++ b/example/meta/abi.cpp @@ -380,4 +380,4 @@ int main() { std::cout << "End of ABI Parsing Tool Library Example\n" << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/meta/any.cpp b/example/meta/any.cpp index f5dd9402..6c1c366a 100644 --- a/example/meta/any.cpp +++ b/example/meta/any.cpp @@ -469,4 +469,4 @@ int main() { << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/meta/anymeta.cpp b/example/meta/anymeta.cpp index fea5344a..074bcf58 100644 --- a/example/meta/anymeta.cpp +++ b/example/meta/anymeta.cpp @@ -900,4 +900,4 @@ int main() { std::cout << "Cleanup complete." << std::endl; std::cout << "=========================================" << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/meta/bind_first.cpp b/example/meta/bind_first.cpp index 84c89265..cebd5082 100644 --- a/example/meta/bind_first.cpp +++ b/example/meta/bind_first.cpp @@ -951,4 +951,4 @@ int main() { } }; -} \ No newline at end of file +} diff --git a/example/meta/concept.cpp b/example/meta/concept.cpp index cbc6d702..638a93bb 100644 --- a/example/meta/concept.cpp +++ b/example/meta/concept.cpp @@ -747,4 +747,4 @@ int main() { std::cout << "All examples completed successfully!" << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/meta/constructor.cpp b/example/meta/constructor.cpp index b398fee5..964a359f 100644 --- a/example/meta/constructor.cpp +++ b/example/meta/constructor.cpp @@ -605,4 +605,4 @@ int main() { std::cout << "\nAll examples completed successfully!" << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/meta/conversion.cpp b/example/meta/conversion.cpp index 6e08e464..41cbedbd 100644 --- a/example/meta/conversion.cpp +++ b/example/meta/conversion.cpp @@ -828,4 +828,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/meta/decorate.cpp b/example/meta/decorate.cpp index 31d30b1a..963e014b 100644 --- a/example/meta/decorate.cpp +++ b/example/meta/decorate.cpp @@ -564,4 +564,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/meta/enum.cpp b/example/meta/enum.cpp index 302aa442..0cf4b587 100644 --- a/example/meta/enum.cpp +++ b/example/meta/enum.cpp @@ -466,4 +466,4 @@ int main() { filePerms = checkAndUpdatePermissions(filePerms, false); // Remove execute return 0; -} \ No newline at end of file +} diff --git a/example/meta/ffi.cpp b/example/meta/ffi.cpp index 4f1a93d1..551a8ae5 100644 --- a/example/meta/ffi.cpp +++ b/example/meta/ffi.cpp @@ -360,7 +360,7 @@ int main() { std::cout << R"( DynamicLibrary myLibrary("path/to/library.so", {}); auto mockObjectResult = myLibrary.createObject("createMockLibrary"); - + if (mockObjectResult) { MockLibraryInterface& mockObj = *mockObjectResult.value(); int sum = mockObj.add(10, 20); @@ -554,4 +554,4 @@ int main() { std::cout << "10. Low-level FFI wrapper for direct control" << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/meta/field_count.cpp b/example/meta/field_count.cpp index 057f00ac..3b99354e 100644 --- a/example/meta/field_count.cpp +++ b/example/meta/field_count.cpp @@ -369,4 +369,4 @@ int main() { << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/meta/func_traits.cpp b/example/meta/func_traits.cpp index ec37a224..885c4cc5 100644 --- a/example/meta/func_traits.cpp +++ b/example/meta/func_traits.cpp @@ -553,4 +553,4 @@ int main() { executeFunction("add", "wrong", "types"); // Intentional type mismatch return 0; -} \ No newline at end of file +} diff --git a/example/meta/global_ptr.cpp b/example/meta/global_ptr.cpp index 260547a1..ea25b127 100644 --- a/example/meta/global_ptr.cpp +++ b/example/meta/global_ptr.cpp @@ -598,4 +598,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/meta/god.cpp b/example/meta/god.cpp index de66817d..35dc16a1 100644 --- a/example/meta/god.cpp +++ b/example/meta/god.cpp @@ -870,4 +870,4 @@ void demonstrateResourceManagement() { std::cout << std::endl; } -} \ No newline at end of file +} diff --git a/example/meta/invoke.cpp b/example/meta/invoke.cpp index d83faf0c..c1575128 100644 --- a/example/meta/invoke.cpp +++ b/example/meta/invoke.cpp @@ -822,4 +822,4 @@ void demo_instrumentation() { std::cout << " Instrumentation report for divide_function:\n"; std::cout << " - Would show 4 calls, 2 exceptions\n"; } -} \ No newline at end of file +} diff --git a/example/meta/member.cpp b/example/meta/member.cpp index a1f7b863..9516d169 100644 --- a/example/meta/member.cpp +++ b/example/meta/member.cpp @@ -393,4 +393,4 @@ int main() { std::cout << "\n"; return 0; -} \ No newline at end of file +} diff --git a/example/meta/overload.cpp b/example/meta/overload.cpp index 4ba37aa5..f25395fe 100644 --- a/example/meta/overload.cpp +++ b/example/meta/overload.cpp @@ -270,4 +270,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/meta/property.cpp b/example/meta/property.cpp index 798c6cf0..2aeb9a03 100644 --- a/example/meta/property.cpp +++ b/example/meta/property.cpp @@ -396,4 +396,4 @@ int main() { << "°C = " << fahrenheit << "°F" << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/meta/proxy.cpp b/example/meta/proxy.cpp index cdda1659..212dd42b 100644 --- a/example/meta/proxy.cpp +++ b/example/meta/proxy.cpp @@ -479,4 +479,4 @@ int main() { << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/meta/proxy_params.cpp b/example/meta/proxy_params.cpp index 14c01b52..596f7917 100644 --- a/example/meta/proxy_params.cpp +++ b/example/meta/proxy_params.cpp @@ -478,4 +478,4 @@ int main() { printJson(complexJson); return 0; -} \ No newline at end of file +} diff --git a/example/meta/raw_name.cpp b/example/meta/raw_name.cpp index 568c7d53..3feb3b6a 100644 --- a/example/meta/raw_name.cpp +++ b/example/meta/raw_name.cpp @@ -311,4 +311,4 @@ int main() { "test"); // Should not match (std::string vs const char*) return 0; -} \ No newline at end of file +} diff --git a/example/meta/signature.cpp b/example/meta/signature.cpp index b3585afe..3818f471 100644 --- a/example/meta/signature.cpp +++ b/example/meta/signature.cpp @@ -356,4 +356,4 @@ int main() { "Matrix, parallelism: int = 4) -> Matrix noexcept"); return 0; -} \ No newline at end of file +} diff --git a/example/meta/stepper.cpp b/example/meta/stepper.cpp index d0d2cbbf..3460979d 100644 --- a/example/meta/stepper.cpp +++ b/example/meta/stepper.cpp @@ -579,4 +579,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/meta/template_traits.cpp b/example/meta/template_traits.cpp index b16d56cf..67fb1c62 100644 --- a/example/meta/template_traits.cpp +++ b/example/meta/template_traits.cpp @@ -495,4 +495,4 @@ int main() { showTemplateInfo(var); return 0; -} \ No newline at end of file +} diff --git a/example/meta/type_caster.cpp b/example/meta/type_caster.cpp index 872cff8c..4d38f3ac 100644 --- a/example/meta/type_caster.cpp +++ b/example/meta/type_caster.cpp @@ -550,4 +550,4 @@ int main() { std::cout << "\nAll TypeCaster examples completed successfully!\n"; return 0; -} \ No newline at end of file +} diff --git a/example/meta/type_info.cpp b/example/meta/type_info.cpp index 7d57ea3d..6977587c 100644 --- a/example/meta/type_info.cpp +++ b/example/meta/type_info.cpp @@ -563,4 +563,4 @@ int main() { std::cout << "\nAll TypeInfo examples completed successfully!\n"; return 0; -} \ No newline at end of file +} diff --git a/example/meta/vany.cpp b/example/meta/vany.cpp index 106670bd..4e1585ad 100644 --- a/example/meta/vany.cpp +++ b/example/meta/vany.cpp @@ -560,4 +560,4 @@ int main() { std::cout << "\nAll Any examples completed successfully!\n"; return 0; -} \ No newline at end of file +} diff --git a/example/search/CMakeLists.txt b/example/search/CMakeLists.txt index d54f820a..ad38a495 100644 --- a/example/search/CMakeLists.txt +++ b/example/search/CMakeLists.txt @@ -14,20 +14,20 @@ file(GLOB CPP_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) foreach(CPP_FILE ${CPP_FILES}) # 获取不带扩展名的文件名 get_filename_component(EXAMPLE_NAME ${CPP_FILE} NAME_WE) - + # 构造可执行文件名称(子目录名_文件名) set(EXECUTABLE_NAME ${SUBDIR_NAME}_${EXAMPLE_NAME}) - + # 配置选项,允许单独控制每个示例的构建 string(TOUPPER ${EXAMPLE_NAME} EXAMPLE_NAME_UPPER) option(ATOM_EXAMPLE_SEARCH_${EXAMPLE_NAME_UPPER} "Build search example: ${EXAMPLE_NAME}" ${ATOM_EXAMPLE_SEARCH_BUILD_ALL}) - + # 有条件地添加可执行文件 if(ATOM_EXAMPLE_SEARCH_${EXAMPLE_NAME_UPPER}) add_executable(${EXECUTABLE_NAME} ${CPP_FILE}) target_link_libraries(${EXECUTABLE_NAME} atom) - - + + # 设置IDE文件夹分组 set_property(TARGET ${EXECUTABLE_NAME} PROPERTY FOLDER "Examples/Search") endif() diff --git a/example/search/cache.cpp b/example/search/cache.cpp index 4db22626..d6ea85b7 100644 --- a/example/search/cache.cpp +++ b/example/search/cache.cpp @@ -650,4 +650,4 @@ int main() { std::cout << "Example completed successfully!" << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/search/lru.cpp b/example/search/lru.cpp index 35635234..fd6887ac 100644 --- a/example/search/lru.cpp +++ b/example/search/lru.cpp @@ -669,4 +669,4 @@ int main() { std::cout << " 10. Error handling and edge cases" << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/search/search.cpp b/example/search/search.cpp index e4bd46de..8f8d579e 100644 --- a/example/search/search.cpp +++ b/example/search/search.cpp @@ -617,4 +617,4 @@ int main() { std::cout << " 8. Multithreaded search operations" << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/search/sqlite.cpp b/example/search/sqlite.cpp index c6c9523d..331bc1aa 100644 --- a/example/search/sqlite.cpp +++ b/example/search/sqlite.cpp @@ -313,4 +313,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/search/ttl.cpp b/example/search/ttl.cpp index 148c4526..47f6d510 100644 --- a/example/search/ttl.cpp +++ b/example/search/ttl.cpp @@ -265,4 +265,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/serial/CMakeLists.txt b/example/serial/CMakeLists.txt index acbe9f52..a20ac4d3 100644 --- a/example/serial/CMakeLists.txt +++ b/example/serial/CMakeLists.txt @@ -14,20 +14,20 @@ file(GLOB CPP_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) foreach(CPP_FILE ${CPP_FILES}) # 获取不带扩展名的文件名 get_filename_component(EXAMPLE_NAME ${CPP_FILE} NAME_WE) - + # 构造可执行文件名称(子目录名_文件名) set(EXECUTABLE_NAME ${SUBDIR_NAME}_${EXAMPLE_NAME}) - + # 配置选项,允许单独控制每个示例的构建 string(TOUPPER ${EXAMPLE_NAME} EXAMPLE_NAME_UPPER) option(ATOM_EXAMPLE_SERIAL_${EXAMPLE_NAME_UPPER} "Build serial example: ${EXAMPLE_NAME}" ${ATOM_EXAMPLE_SERIAL_BUILD_ALL}) - + # 有条件地添加可执行文件 if(ATOM_EXAMPLE_SERIAL_${EXAMPLE_NAME_UPPER}) add_executable(${EXECUTABLE_NAME} ${CPP_FILE}) target_link_libraries(${EXECUTABLE_NAME} atom) - - + + # 设置IDE文件夹分组 set_property(TARGET ${EXECUTABLE_NAME} PROPERTY FOLDER "Examples/Serial") endif() diff --git a/example/serial/scanner.cpp b/example/serial/scanner.cpp index dc126026..bd9f82f0 100644 --- a/example/serial/scanner.cpp +++ b/example/serial/scanner.cpp @@ -9,7 +9,7 @@ using namespace atom::serial; /** * 展示如何使用 SerialPortScanner 类的基本功能 - * + * * 本示例展示: * 1. 异步列出可用端口 * 2. 获取特定端口的详细信息 @@ -26,41 +26,41 @@ int main() { config.detect_ch340 = true; config.include_virtual_ports = false; config.timeout = std::chrono::milliseconds(2000); - + SerialPortScanner scanner(config); - + // 注册一个自定义设备检测器 scanner.register_device_detector( - "FTDI", + "FTDI", [](uint16_t vid, uint16_t pid, std::string_view description) -> std::pair { // 检测是否为FTDI设备 if (vid == 0x0403) { return {true, "FTDI Device"}; } - + // 检查描述 std::string lower_desc; lower_desc.resize(description.size()); std::transform(description.begin(), description.end(), lower_desc.begin(), [](unsigned char c) { return std::tolower(c); }); - + if (lower_desc.find("ftdi") != std::string::npos) { return {true, "FTDI (Detected by Description)"}; } - + return {false, ""}; } ); - + // 异步列出可用端口 std::cout << "正在异步列出可用端口...\n"; std::atomic done = false; - + scanner.list_available_ports_async( [&done](SerialPortScanner::Result> result) { if (std::holds_alternative>(result)) { const auto& ports = std::get>(result); - + std::cout << "找到 " << ports.size() << " 个串口:\n"; for (const auto& port : ports) { std::cout << " - " << port.device << ": " << port.description; @@ -71,36 +71,36 @@ int main() { } } else { const auto& error = std::get(result); - std::cerr << "错误: " << error.message + std::cerr << "错误: " << error.message << " (代码: " << error.error_code << ")\n"; } - + done = true; } ); - + // 等待异步操作完成 while (!done) { std::cout << "等待扫描完成...\n"; std::this_thread::sleep_for(std::chrono::milliseconds(500)); } - + std::cout << "\n正在同步列出可用端口...\n"; auto sync_result = scanner.list_available_ports(); - + if (std::holds_alternative>(sync_result)) { const auto& ports = std::get>(sync_result); - + // 如果找到端口,获取第一个端口的详细信息 if (!ports.empty()) { std::string first_port = ports.front().device; std::cout << "\n获取 " << first_port << " 的详细信息:\n"; - + auto details_result = scanner.get_port_details(first_port); - + if (std::holds_alternative>(details_result)) { const auto& maybe_details = std::get>(details_result); - + if (maybe_details) { const auto& details = *maybe_details; std::cout << " 设备名称: " << details.device_name << "\n"; @@ -108,22 +108,22 @@ int main() { std::cout << " 硬件 ID: " << details.hardware_id << "\n"; std::cout << " VID: " << details.vid << "\n"; std::cout << " PID: " << details.pid << "\n"; - + if (!details.serial_number.empty()) std::cout << " 序列号: " << details.serial_number << "\n"; - + if (!details.manufacturer.empty()) std::cout << " 制造商: " << details.manufacturer << "\n"; - + if (!details.product.empty()) std::cout << " 产品: " << details.product << "\n"; - + if (!details.location.empty()) std::cout << " 位置: " << details.location << "\n"; - + if (!details.interface.empty()) std::cout << " 接口: " << details.interface << "\n"; - + if (details.is_ch340) { std::cout << " CH340 设备: " << details.ch340_model << "\n"; std::cout << " 推荐波特率: " << details.recommended_baud_rates << "\n"; @@ -134,16 +134,16 @@ int main() { } } else { const auto& error = std::get(details_result); - std::cerr << "获取详细信息时出错: " << error.message + std::cerr << "获取详细信息时出错: " << error.message << " (代码: " << error.error_code << ")\n"; } } } - + } catch (const std::exception& e) { std::cerr << "异常: " << e.what() << "\n"; return 1; } - + return 0; -} \ No newline at end of file +} diff --git a/example/system/CMakeLists.txt b/example/system/CMakeLists.txt index ed517df5..304f8021 100644 --- a/example/system/CMakeLists.txt +++ b/example/system/CMakeLists.txt @@ -14,20 +14,20 @@ file(GLOB CPP_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) foreach(CPP_FILE ${CPP_FILES}) # 获取不带扩展名的文件名 get_filename_component(EXAMPLE_NAME ${CPP_FILE} NAME_WE) - + # 构造可执行文件名称(子目录名_文件名) set(EXECUTABLE_NAME ${SUBDIR_NAME}_${EXAMPLE_NAME}) - + # 配置选项,允许单独控制每个示例的构建 string(TOUPPER ${EXAMPLE_NAME} EXAMPLE_NAME_UPPER) option(ATOM_EXAMPLE_SYSTEM_${EXAMPLE_NAME_UPPER} "Build system example: ${EXAMPLE_NAME}" ${ATOM_EXAMPLE_SYSTEM_BUILD_ALL}) - + # 有条件地添加可执行文件 if(ATOM_EXAMPLE_SYSTEM_${EXAMPLE_NAME_UPPER}) add_executable(${EXECUTABLE_NAME} ${CPP_FILE}) target_link_libraries(${EXECUTABLE_NAME} atom) - - + + # 设置IDE文件夹分组 set_property(TARGET ${EXECUTABLE_NAME} PROPERTY FOLDER "Examples/System") endif() diff --git a/example/system/command.cpp b/example/system/command.cpp index c1b9ba46..100bbad5 100644 --- a/example/system/command.cpp +++ b/example/system/command.cpp @@ -236,4 +236,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/system/crash_quotes.cpp b/example/system/crash_quotes.cpp index ca3c8470..47f1ec8a 100644 --- a/example/system/crash_quotes.cpp +++ b/example/system/crash_quotes.cpp @@ -58,4 +58,4 @@ int main() { manager.saveQuotesToJson("saved_quotes.json"); return 0; -} \ No newline at end of file +} diff --git a/example/system/crontab.cpp b/example/system/crontab.cpp index f68382f4..4ef9e116 100644 --- a/example/system/crontab.cpp +++ b/example/system/crontab.cpp @@ -56,4 +56,4 @@ int main() { std::cout << "Cron job statistics: " << stats << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/system/env.cpp b/example/system/env.cpp index bd5f905a..b606b694 100644 --- a/example/system/env.cpp +++ b/example/system/env.cpp @@ -62,4 +62,4 @@ int main(int argc, char** argv) { std::cout << "Added SHARED_VAR=789 to shared Env" << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/system/gpio.cpp b/example/system/gpio.cpp index 59574f4c..793e6854 100644 --- a/example/system/gpio.cpp +++ b/example/system/gpio.cpp @@ -27,4 +27,4 @@ int main() { }); return 0; -} \ No newline at end of file +} diff --git a/example/system/lregistry.cpp b/example/system/lregistry.cpp index ea1fe761..a0f16e7a 100644 --- a/example/system/lregistry.cpp +++ b/example/system/lregistry.cpp @@ -58,4 +58,4 @@ int main() { std::cout << "Registry data restored from backup file" << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/system/network_manager.cpp b/example/system/network_manager.cpp index 7f9d935b..ea3fb7ef 100644 --- a/example/system/network_manager.cpp +++ b/example/system/network_manager.cpp @@ -74,4 +74,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/system/pidwatcher.cpp b/example/system/pidwatcher.cpp index b8e1cff0..f204c7db 100644 --- a/example/system/pidwatcher.cpp +++ b/example/system/pidwatcher.cpp @@ -43,4 +43,4 @@ int main() { std::cout << "Monitoring stopped." << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/system/priority.cpp b/example/system/priority.cpp index f09f11e5..b4a87e07 100644 --- a/example/system/priority.cpp +++ b/example/system/priority.cpp @@ -57,4 +57,4 @@ int main() { std::this_thread::sleep_for(std::chrono::seconds(5)); return 0; -} \ No newline at end of file +} diff --git a/example/system/process.cpp b/example/system/process.cpp index f09d6084..0046ab8e 100644 --- a/example/system/process.cpp +++ b/example/system/process.cpp @@ -62,4 +62,4 @@ int main() { #endif return 0; -} \ No newline at end of file +} diff --git a/example/system/process_manager.cpp b/example/system/process_manager.cpp index 5b27c739..dcb5ef4d 100644 --- a/example/system/process_manager.cpp +++ b/example/system/process_manager.cpp @@ -105,4 +105,4 @@ int main() { #endif return 0; -} \ No newline at end of file +} diff --git a/example/system/signal.cpp b/example/system/signal.cpp index 6fcb6bf9..1152b18a 100644 --- a/example/system/signal.cpp +++ b/example/system/signal.cpp @@ -58,4 +58,4 @@ int main() { std::this_thread::sleep_for(std::chrono::seconds(1)); return 0; -} \ No newline at end of file +} diff --git a/example/system/software.cpp b/example/system/software.cpp index b2745cb5..5b21d72b 100644 --- a/example/system/software.cpp +++ b/example/system/software.cpp @@ -28,4 +28,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/system/stat.cpp b/example/system/stat.cpp index 661f9728..85310a9f 100644 --- a/example/system/stat.cpp +++ b/example/system/stat.cpp @@ -58,4 +58,4 @@ int main() { std::cout << "File path: " << filePathRetrieved << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/system/storage.cpp b/example/system/storage.cpp index 022a7cee..d4038a4a 100644 --- a/example/system/storage.cpp +++ b/example/system/storage.cpp @@ -60,4 +60,4 @@ int main() { std::cout << "Stopped monitoring" << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/system/user.cpp b/example/system/user.cpp index 08a44a28..7e798dd7 100644 --- a/example/system/user.cpp +++ b/example/system/user.cpp @@ -57,4 +57,4 @@ int main() { std::cout << "Is root user: " << std::boolalpha << isRootUser << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/system/wregistry.cpp b/example/system/wregistry.cpp index 433c2c09..24154c70 100644 --- a/example/system/wregistry.cpp +++ b/example/system/wregistry.cpp @@ -69,4 +69,4 @@ int main() { #endif return 0; -} \ No newline at end of file +} diff --git a/example/type/CMakeLists.txt b/example/type/CMakeLists.txt index 7de8c56d..5c1c4897 100644 --- a/example/type/CMakeLists.txt +++ b/example/type/CMakeLists.txt @@ -14,20 +14,20 @@ file(GLOB CPP_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) foreach(CPP_FILE ${CPP_FILES}) # 获取不带扩展名的文件名 get_filename_component(EXAMPLE_NAME ${CPP_FILE} NAME_WE) - + # 构造可执行文件名称(子目录名_文件名) set(EXECUTABLE_NAME ${SUBDIR_NAME}_${EXAMPLE_NAME}) - + # 配置选项,允许单独控制每个示例的构建 string(TOUPPER ${EXAMPLE_NAME} EXAMPLE_NAME_UPPER) option(ATOM_EXAMPLE_TYPE_${EXAMPLE_NAME_UPPER} "Build type example: ${EXAMPLE_NAME}" ${ATOM_EXAMPLE_TYPE_BUILD_ALL}) - + # 有条件地添加可执行文件 if(ATOM_EXAMPLE_TYPE_${EXAMPLE_NAME_UPPER}) add_executable(${EXECUTABLE_NAME} ${CPP_FILE}) target_link_libraries(${EXECUTABLE_NAME} atom) - - + + # 设置IDE文件夹分组 set_property(TARGET ${EXECUTABLE_NAME} PROPERTY FOLDER "Examples/Type") endif() diff --git a/example/type/args.cpp b/example/type/args.cpp index d6de0e00..b5c85d57 100644 --- a/example/type/args.cpp +++ b/example/type/args.cpp @@ -899,4 +899,4 @@ int main() { << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/type/argsview.cpp b/example/type/argsview.cpp index f1f441d4..db10c03e 100644 --- a/example/type/argsview.cpp +++ b/example/type/argsview.cpp @@ -366,4 +366,4 @@ int main() { std::cout << "\nAll examples completed successfully!" << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/type/auto_table.cpp b/example/type/auto_table.cpp index 3d8c4e13..c9e72bc2 100644 --- a/example/type/auto_table.cpp +++ b/example/type/auto_table.cpp @@ -391,4 +391,4 @@ int main() { std::cout << "\nAll examples completed successfully!" << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/type/concurrent_map.cpp b/example/type/concurrent_map.cpp index 9460d03a..f454b42f 100644 --- a/example/type/concurrent_map.cpp +++ b/example/type/concurrent_map.cpp @@ -311,4 +311,4 @@ int main() { std::cout << "\nAll examples completed successfully!" << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/type/concurrent_set.cpp b/example/type/concurrent_set.cpp index 44cd5a3f..b12cad8f 100644 --- a/example/type/concurrent_set.cpp +++ b/example/type/concurrent_set.cpp @@ -791,4 +791,4 @@ int main() { std::cout << "\nExample completed successfully!" << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/type/concurrent_vector.cpp b/example/type/concurrent_vector.cpp index b8dcde8e..362fb60b 100644 --- a/example/type/concurrent_vector.cpp +++ b/example/type/concurrent_vector.cpp @@ -667,4 +667,4 @@ int main() { << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/type/cstream.cpp b/example/type/cstream.cpp index e62e5a28..3258f235 100644 --- a/example/type/cstream.cpp +++ b/example/type/cstream.cpp @@ -598,4 +598,4 @@ int main() { << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/type/expected.cpp b/example/type/expected.cpp index d3fe08f4..b412927a 100644 --- a/example/type/expected.cpp +++ b/example/type/expected.cpp @@ -725,4 +725,4 @@ int main() { << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/type/flatmap.cpp b/example/type/flatmap.cpp index 8578413a..e422eeb4 100644 --- a/example/type/flatmap.cpp +++ b/example/type/flatmap.cpp @@ -354,4 +354,4 @@ int main() { sorted_vector_example(); return 0; -} \ No newline at end of file +} diff --git a/example/type/flatset.cpp b/example/type/flatset.cpp index a60ef2a2..b8c1a0c0 100644 --- a/example/type/flatset.cpp +++ b/example/type/flatset.cpp @@ -565,4 +565,4 @@ int main() { error_handling(); return 0; -} \ No newline at end of file +} diff --git a/example/type/indestructible.cpp b/example/type/indestructible.cpp index adb41c51..fdbc7440 100644 --- a/example/type/indestructible.cpp +++ b/example/type/indestructible.cpp @@ -450,4 +450,4 @@ int main() { std::cout << "\nAll examples completed!\n"; return 0; -} \ No newline at end of file +} diff --git a/example/type/iter.cpp b/example/type/iter.cpp index 72e99684..802e6b61 100644 --- a/example/type/iter.cpp +++ b/example/type/iter.cpp @@ -29,34 +29,34 @@ void print_key_value_container(const Container& container, const std::string& na // Example 1: PointerIterator void pointer_iterator_example() { std::cout << "\n=== Example 1: PointerIterator ===\n"; - + // Create a sample container std::vector numbers = {10, 20, 30, 40, 50}; print_container(numbers, "Original vector"); - + // Create pointer iterators auto [begin_ptr, end_ptr] = makePointerRange(numbers.begin(), numbers.end()); - + // Print addresses of original elements std::cout << "Addresses of elements:\n"; for (auto it = begin_ptr; it != end_ptr; ++it) { int* ptr = *it; // Get a pointer to the element std::cout << "Value: " << *ptr << ", Address: " << ptr << std::endl; } - + // Modify elements via pointers std::cout << "\nModifying elements via pointers...\n"; for (auto it = begin_ptr; it != end_ptr; ++it) { int* ptr = *it; *ptr *= 2; // Double each value } - + print_container(numbers, "Modified vector"); - + // Example of processContainer function std::list chars = {'a', 'b', 'c', 'd', 'e'}; print_container(chars, "Original list of chars"); - + std::cout << "Calling processContainer to remove middle elements...\n"; processContainer(chars); print_container(chars, "Resulting list of chars"); @@ -65,30 +65,30 @@ void pointer_iterator_example() { // Example 2: EarlyIncIterator void early_inc_iterator_example() { std::cout << "\n=== Example 2: EarlyIncIterator ===\n"; - + std::vector numbers = {1, 2, 3, 4, 5}; print_container(numbers, "Original vector"); - + // Create early increment iterators auto begin_early = makeEarlyIncIterator(numbers.begin()); auto end_early = makeEarlyIncIterator(numbers.end()); - + std::cout << "Using EarlyIncIterator to traverse the vector:\n"; for (auto it = begin_early; it != end_early; ++it) { std::cout << *it << " "; } std::cout << std::endl; - + // Demonstrate the early increment behavior std::cout << "\nDemonstrating early increment behavior:\n"; auto it = makeEarlyIncIterator(numbers.begin()); std::cout << "Initial value: " << *it << std::endl; - + // Post increment returns iterator before increment auto copy = it++; std::cout << "After post-increment, original iterator: " << *it << std::endl; std::cout << "Returned copy: " << *copy << std::endl; - + // Pre increment returns reference to incremented iterator auto& ref = ++it; std::cout << "After pre-increment: " << *it << std::endl; @@ -98,37 +98,37 @@ void early_inc_iterator_example() { // Example 3: TransformIterator void transform_iterator_example() { std::cout << "\n=== Example 3: TransformIterator ===\n"; - + std::vector numbers = {1, 2, 3, 4, 5}; print_container(numbers, "Original vector"); - + // Square function auto square = [](int n) { return n * n; }; - + // Create transform iterators that will square each element auto begin_transform = makeTransformIterator(numbers.begin(), square); auto end_transform = makeTransformIterator(numbers.end(), square); - + std::cout << "Squared values using TransformIterator: "; for (auto it = begin_transform; it != end_transform; ++it) { std::cout << *it << " "; // Will print squared values } std::cout << std::endl; - + // Transform strings to their lengths std::vector strings = {"hello", "world", "custom", "iterators", "example"}; print_container(strings, "Original strings"); - + auto string_length = [](const std::string& s) { return s.length(); }; auto begin_length = makeTransformIterator(strings.begin(), string_length); auto end_length = makeTransformIterator(strings.end(), string_length); - + std::cout << "String lengths using TransformIterator: "; for (auto it = begin_length; it != end_length; ++it) { std::cout << *it << " "; // Will print string lengths } std::cout << std::endl; - + // Using transform iterator with structured bindings std::map scores = { {"Alice", 95}, @@ -138,15 +138,15 @@ void transform_iterator_example() { {"Eve", 89} }; print_key_value_container(scores, "Original scores"); - + // Transform to formatted strings auto format_score = [](const std::pair& p) -> std::string { return p.first + ": " + std::to_string(p.second) + " points"; }; - + auto begin_format = makeTransformIterator(scores.begin(), format_score); auto end_format = makeTransformIterator(scores.end(), format_score); - + std::cout << "Formatted scores using TransformIterator:\n"; for (auto it = begin_format; it != end_format; ++it) { std::cout << " " << *it << std::endl; @@ -156,46 +156,46 @@ void transform_iterator_example() { // Example 4: FilterIterator void filter_iterator_example() { std::cout << "\n=== Example 4: FilterIterator ===\n"; - + std::vector numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; print_container(numbers, "Original vector"); - + // Filter for even numbers auto is_even = [](int n) { return n % 2 == 0; }; auto begin_even = makeFilterIterator(numbers.begin(), numbers.end(), is_even); auto end_even = makeFilterIterator(numbers.end(), numbers.end(), is_even); - + std::cout << "Even numbers using FilterIterator: "; for (auto it = begin_even; it != end_even; ++it) { std::cout << *it << " "; } std::cout << std::endl; - + // Filter for numbers greater than 5 auto greater_than_5 = [](int n) { return n > 5; }; auto begin_gt5 = makeFilterIterator(numbers.begin(), numbers.end(), greater_than_5); auto end_gt5 = makeFilterIterator(numbers.end(), numbers.end(), greater_than_5); - + std::cout << "Numbers > 5 using FilterIterator: "; for (auto it = begin_gt5; it != end_gt5; ++it) { std::cout << *it << " "; } std::cout << std::endl; - + // Filter strings by length std::vector strings = {"hi", "hello", "a", "world", "cpp", "custom", "iterators"}; print_container(strings, "Original strings"); - + auto length_greater_than_3 = [](const std::string& s) { return s.length() > 3; }; auto begin_str = makeFilterIterator(strings.begin(), strings.end(), length_greater_than_3); auto end_str = makeFilterIterator(strings.end(), strings.end(), length_greater_than_3); - + std::cout << "Strings longer than 3 characters using FilterIterator: "; for (auto it = begin_str; it != end_str; ++it) { std::cout << *it << " "; } std::cout << std::endl; - + // Filter on a map - only show scores above 90 std::map scores = { {"Alice", 95}, @@ -204,11 +204,11 @@ void filter_iterator_example() { {"David", 78}, {"Eve", 89} }; - + auto high_score = [](const std::pair& p) { return p.second >= 90; }; auto begin_high = makeFilterIterator(scores.begin(), scores.end(), high_score); auto end_high = makeFilterIterator(scores.end(), scores.end(), high_score); - + std::cout << "High scorers (>= 90) using FilterIterator: "; for (auto it = begin_high; it != end_high; ++it) { std::cout << it->first << "(" << it->second << ") "; @@ -219,40 +219,40 @@ void filter_iterator_example() { // Example 5: ReverseIterator void reverse_iterator_example() { std::cout << "\n=== Example 5: ReverseIterator ===\n"; - + std::vector numbers = {1, 2, 3, 4, 5}; print_container(numbers, "Original vector"); - + // Create reverse iterators ReverseIterator::iterator> rbegin(numbers.end()); ReverseIterator::iterator> rend(numbers.begin()); - + std::cout << "Vector traversed in reverse using ReverseIterator: "; for (auto it = rbegin; it != rend; ++it) { std::cout << *it << " "; } std::cout << std::endl; - + // Compare with STL reverse iterator std::cout << "Vector traversed with STL reverse_iterator: "; for (auto it = numbers.rbegin(); it != numbers.rend(); ++it) { std::cout << *it << " "; } std::cout << std::endl; - + // Modify elements using the custom reverse iterator std::cout << "Modifying elements using ReverseIterator...\n"; for (auto it = rbegin; it != rend; ++it) { *it += 10; } print_container(numbers, "Modified vector"); - + // Get underlying iterator using base() std::cout << "Using base() to get the original iterator:\n"; auto rev_it = rbegin; ++rev_it; // Move to the second element from the end auto base_it = rev_it.base(); // Get the forward iterator - + std::cout << "Reverse iterator points to: " << *rev_it << std::endl; std::cout << "Base iterator points to: " << *(base_it - 1) << std::endl; } @@ -260,57 +260,57 @@ void reverse_iterator_example() { // Example 6: ZipIterator void zip_iterator_example() { std::cout << "\n=== Example 6: ZipIterator ===\n"; - + std::vector numbers = {1, 2, 3, 4, 5}; std::vector names = {"one", "two", "three", "four", "five"}; std::vector letters = {'a', 'b', 'c', 'd', 'e'}; - + print_container(numbers, "Numbers"); print_container(names, "Names"); print_container(letters, "Letters"); - + // Create zip iterators for two containers auto begin_zip2 = makeZipIterator(numbers.begin(), names.begin()); auto end_zip2 = makeZipIterator(numbers.end(), names.end()); - + std::cout << "\nZipping numbers and names:\n"; for (auto it = begin_zip2; it != end_zip2; ++it) { auto [num, name] = *it; // Unpack the tuple std::cout << num << ": " << name << std::endl; } - + // Create zip iterators for three containers auto begin_zip3 = makeZipIterator(numbers.begin(), names.begin(), letters.begin()); auto end_zip3 = makeZipIterator(numbers.end(), names.end(), letters.end()); - + std::cout << "\nZipping numbers, names, and letters:\n"; for (auto it = begin_zip3; it != end_zip3; ++it) { auto [num, name, letter] = *it; // Unpack the tuple std::cout << num << ": " << name << " (" << letter << ")" << std::endl; } - + // Use zip iterator to modify elements std::vector vec1 = {1, 2, 3, 4}; std::vector vec2 = {10, 20, 30, 40}; - + std::cout << "\nBefore modification:\n"; print_container(vec1, "Vector 1"); print_container(vec2, "Vector 2"); - + auto begin_mod = makeZipIterator(vec1.begin(), vec2.begin()); auto end_mod = makeZipIterator(vec1.end(), vec2.end()); - + // Sum corresponding elements from vec2 into vec1 for (auto it = begin_mod; it != end_mod; ++it) { const auto& [v1, v2] = *it; // Now correctly bound with const reference // This is just to demonstrate the concept } - + // The correct way to modify elements is to manually unpack and modify for (size_t i = 0; i < vec1.size(); ++i) { vec1[i] += vec2[i]; } - + std::cout << "\nAfter modification (vec1 += vec2):\n"; print_container(vec1, "Vector 1"); print_container(vec2, "Vector 2"); @@ -319,51 +319,51 @@ void zip_iterator_example() { // Example 7: Combining different iterators void combined_iterators_example() { std::cout << "\n=== Example 7: Combining Different Iterators ===\n"; - + std::vector numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; print_container(numbers, "Original vector"); - + // 1. Filter for even numbers, then transform to squares auto is_even = [](int n) { return n % 2 == 0; }; auto square = [](int n) { return n * n; }; - + auto begin_filter = makeFilterIterator(numbers.begin(), numbers.end(), is_even); auto end_filter = makeFilterIterator(numbers.end(), numbers.end(), is_even); - + auto begin_combined = makeTransformIterator(begin_filter, square); auto end_combined = makeTransformIterator(end_filter, square); - + std::cout << "Squares of even numbers: "; for (auto it = begin_combined; it != end_combined; ++it) { std::cout << *it << " "; // Should print 4, 16, 36, 64, 100 } std::cout << std::endl; - + // 2. Create pointers to the elements, then filter by value std::cout << "\nPointing to elements greater than 5:\n"; - + auto [begin_ptr, end_ptr] = makePointerRange(numbers.begin(), numbers.end()); - + auto value_gt_5 = [](int* ptr) { return *ptr > 5; }; auto begin_ptr_filter = makeFilterIterator(begin_ptr, end_ptr, value_gt_5); auto end_ptr_filter = makeFilterIterator(end_ptr, end_ptr, value_gt_5); - + for (auto it = begin_ptr_filter; it != end_ptr_filter; ++it) { int* ptr = *it; std::cout << "Value: " << *ptr << ", Address: " << ptr << std::endl; } - + // 3. Combine transform and zip std::vector names = {"Alice", "Bob", "Charlie", "David", "Eve"}; std::vector ages = {25, 30, 35, 40, 45}; - + auto name_to_length = [](const std::string& s) { return s.length(); }; auto begin_name_len = makeTransformIterator(names.begin(), name_to_length); auto end_name_len = makeTransformIterator(names.end(), name_to_length); - + auto begin_combined_zip = makeZipIterator(begin_name_len, ages.begin()); auto end_combined_zip = makeZipIterator(end_name_len, ages.end()); - + std::cout << "\nName lengths paired with ages:\n"; for (auto it = begin_combined_zip; it != end_combined_zip; ++it) { auto [length, age] = *it; @@ -373,7 +373,7 @@ void combined_iterators_example() { int main() { std::cout << "===== Custom Iterator Examples =====\n"; - + pointer_iterator_example(); early_inc_iterator_example(); transform_iterator_example(); @@ -381,6 +381,6 @@ int main() { reverse_iterator_example(); zip_iterator_example(); combined_iterators_example(); - + return 0; -} \ No newline at end of file +} diff --git a/example/type/json-schema.cpp b/example/type/json-schema.cpp index 128bb6da..45b33a87 100644 --- a/example/type/json-schema.cpp +++ b/example/type/json-schema.cpp @@ -52,4 +52,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/type/no_offset_ptr.cpp b/example/type/no_offset_ptr.cpp index bdc9030f..ae4a1b8f 100644 --- a/example/type/no_offset_ptr.cpp +++ b/example/type/no_offset_ptr.cpp @@ -466,4 +466,4 @@ int main() { std::cout << "\nAll examples completed successfully!\n"; return 0; -} \ No newline at end of file +} diff --git a/example/type/optional.cpp b/example/type/optional.cpp index d1d7e40b..d145df74 100644 --- a/example/type/optional.cpp +++ b/example/type/optional.cpp @@ -647,4 +647,4 @@ int main() { advancedUsageExample(); return 0; -} \ No newline at end of file +} diff --git a/example/type/pod_vector.cpp b/example/type/pod_vector.cpp index 7b9c996e..078bb659 100644 --- a/example/type/pod_vector.cpp +++ b/example/type/pod_vector.cpp @@ -529,4 +529,4 @@ int main() { advancedUsageExample(); return 0; -} \ No newline at end of file +} diff --git a/example/type/pointer.cpp b/example/type/pointer.cpp index 7220a2eb..22ae6af1 100644 --- a/example/type/pointer.cpp +++ b/example/type/pointer.cpp @@ -700,4 +700,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/type/qvariant.cpp b/example/type/qvariant.cpp index 7ec2c5af..057ac627 100644 --- a/example/type/qvariant.cpp +++ b/example/type/qvariant.cpp @@ -799,4 +799,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/type/rtype.cpp b/example/type/rtype.cpp index 4b2d5bc1..6f97c154 100644 --- a/example/type/rtype.cpp +++ b/example/type/rtype.cpp @@ -98,4 +98,4 @@ int main() { std::cout << "Person YAML: " << newPersonYaml.dump() << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/type/small_list.cpp b/example/type/small_list.cpp index 4187a4d1..4f38e627 100644 --- a/example/type/small_list.cpp +++ b/example/type/small_list.cpp @@ -766,4 +766,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/type/small_vector.cpp b/example/type/small_vector.cpp index de59e965..d5fba399 100644 --- a/example/type/small_vector.cpp +++ b/example/type/small_vector.cpp @@ -841,4 +841,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/type/static_string.cpp b/example/type/static_string.cpp index 8530b394..7f74c06b 100644 --- a/example/type/static_string.cpp +++ b/example/type/static_string.cpp @@ -663,4 +663,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/type/static_vector.cpp b/example/type/static_vector.cpp index e3e706d6..b4b2df18 100644 --- a/example/type/static_vector.cpp +++ b/example/type/static_vector.cpp @@ -831,4 +831,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/type/string.cpp b/example/type/string.cpp index 77dcb54a..3e88e179 100644 --- a/example/type/string.cpp +++ b/example/type/string.cpp @@ -753,4 +753,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/type/trackable.cpp b/example/type/trackable.cpp index aecbf2fa..a145dedd 100644 --- a/example/type/trackable.cpp +++ b/example/type/trackable.cpp @@ -658,4 +658,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/type/uint.cpp b/example/type/uint.cpp index 8a999ffc..8beddbb3 100644 --- a/example/type/uint.cpp +++ b/example/type/uint.cpp @@ -35,4 +35,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/type/weak_ptr.cpp b/example/type/weak_ptr.cpp index 45300523..c76425be 100644 --- a/example/type/weak_ptr.cpp +++ b/example/type/weak_ptr.cpp @@ -25,30 +25,30 @@ class TestObject { TestObject(int id, std::string name) : id_(id), name_(std::move(name)) { std::cout << "TestObject #" << id_ << " (" << name_ << ") constructed" << std::endl; } - + ~TestObject() { std::cout << "TestObject #" << id_ << " (" << name_ << ") destroyed" << std::endl; } - - int getId() const { + + int getId() const { access_count_++; - return id_; + return id_; } - - std::string getName() const { + + std::string getName() const { access_count_++; - return name_; + return name_; } - + void setName(const std::string& name) { access_count_++; name_ = name; } - + int getAccessCount() const { return access_count_.load(); } - + void performOperation() const { access_count_++; std::cout << "Operation performed on TestObject #" << id_ << " (" << name_ << ")" << std::endl; @@ -61,19 +61,19 @@ class DerivedObject : public TestObject { double extra_data_; public: - DerivedObject(int id, std::string name, double extra_data) + DerivedObject(int id, std::string name, double extra_data) : TestObject(id, std::move(name)), extra_data_(extra_data) { std::cout << "DerivedObject with extra_data=" << extra_data_ << " constructed" << std::endl; } - + ~DerivedObject() { std::cout << "DerivedObject with extra_data=" << extra_data_ << " destroyed" << std::endl; } - + double getExtraData() const { return extra_data_; } - + void setExtraData(double value) { extra_data_ = value; } @@ -95,17 +95,17 @@ void basicUsageExample() { // Create a shared_ptr to a TestObject auto shared = std::make_shared(1, "Basic Test"); - + printSubSection("Construction and State Checking"); // Create an EnhancedWeakPtr from the shared_ptr EnhancedWeakPtr weak(shared); - + // Check if the weak pointer is expired std::cout << "Is weak pointer expired? " << (weak.expired() ? "Yes" : "No") << std::endl; - + // Get the use count std::cout << "Use count: " << weak.useCount() << std::endl; - + printSubSection("Locking the Weak Pointer"); // Lock the weak pointer to get a shared_ptr if (auto locked = weak.lock()) { @@ -114,81 +114,81 @@ void basicUsageExample() { } else { std::cout << "Failed to lock weak pointer" << std::endl; } - + printSubSection("Handling Expiration"); // Make the weak pointer expire by resetting the original shared_ptr std::cout << "Resetting original shared_ptr..." << std::endl; shared.reset(); - + // Check if the weak pointer is now expired std::cout << "Is weak pointer expired? " << (weak.expired() ? "Yes" : "No") << std::endl; - + // Try to lock an expired weak pointer if (auto locked = weak.lock()) { std::cout << "Successfully locked weak pointer (shouldn't happen)" << std::endl; } else { std::cout << "Failed to lock expired weak pointer (expected)" << std::endl; } - + printSubSection("Manual Reset"); // Create a new shared_ptr and weak_ptr shared = std::make_shared(2, "Reset Test"); EnhancedWeakPtr resetWeak(shared); - + // Reset the weak pointer manually std::cout << "Manually resetting weak pointer..." << std::endl; resetWeak.reset(); - + // Verify it's expired even though the shared_ptr is still valid std::cout << "Is weak pointer expired after reset? " << (resetWeak.expired() ? "Yes" : "No") << std::endl; std::cout << "Original shared_ptr use count: " << shared.use_count() << std::endl; - + printSubSection("Getting Lock Attempts"); EnhancedWeakPtr lockCounter(shared); - + // Perform several lock attempts for (int i = 0; i < 5; i++) { auto locked = lockCounter.lock(); } - + std::cout << "Number of lock attempts: " << lockCounter.getLockAttempts() << std::endl; } // Example 2: Advanced Locking Techniques void advancedLockingExample() { printSection("Advanced Locking Techniques"); - + // Create a shared_ptr to a TestObject auto shared = std::make_shared(3, "Advanced Lock Test"); - + // Create an EnhancedWeakPtr from the shared_ptr EnhancedWeakPtr weak(shared); - + printSubSection("Using withLock for Safe Access"); // Use withLock to safely access the object auto result = weak.withLock([](TestObject& obj) { std::cout << "Accessing object with ID: " << obj.getId() << std::endl; return obj.getName(); }); - + if (result) { std::cout << "withLock returned: " << *result << std::endl; } else { std::cout << "withLock failed to access the object" << std::endl; } - + // Use withLock with a void return type bool success = weak.withLock([](TestObject& obj) { std::cout << "Performing void operation on object: " << obj.getName() << std::endl; obj.setName("Updated Name"); }); - + std::cout << "Void operation success: " << (success ? "Yes" : "No") << std::endl; - + // Verify the name was updated std::string name = weak.withLock([](TestObject& obj) { return obj.getName(); }).value_or("Unknown"); std::cout << "Updated name: " << name << std::endl; - + printSubSection("tryLockOrElse Method"); // Use tryLockOrElse to handle both success and failure cases auto nameOrDefault = weak.tryLockOrElse( @@ -201,27 +201,27 @@ void advancedLockingExample() { return "Object not available"; } ); - + std::cout << "tryLockOrElse result: " << nameOrDefault << std::endl; - + printSubSection("Periodic Lock Attempts"); // Use tryLockPeriodic to attempt locking periodically std::cout << "Attempting periodic locks (should succeed immediately)..." << std::endl; auto periodicLock = weak.tryLockPeriodic(100ms, 5); - + if (periodicLock) { std::cout << "Successfully obtained lock periodically for: " << periodicLock->getName() << std::endl; } else { std::cout << "Failed to obtain lock after periodic attempts" << std::endl; } - + // Make the object expire shared.reset(); - + // Try periodic locking on an expired pointer std::cout << "Attempting periodic locks on expired pointer..." << std::endl; auto failedLock = weak.tryLockPeriodic(50ms, 3); - + if (failedLock) { std::cout << "Unexpectedly obtained lock" << std::endl; } else { @@ -232,22 +232,22 @@ void advancedLockingExample() { // Example 3: Asynchronous Operations void asynchronousOperationsExample() { printSection("Asynchronous Operations"); - + // Create a shared_ptr to a TestObject auto shared = std::make_shared(4, "Async Test"); - + // Create an EnhancedWeakPtr from the shared_ptr EnhancedWeakPtr weak(shared); - + printSubSection("Async Lock"); // Perform an asynchronous lock std::cout << "Starting async lock operation..." << std::endl; auto future = weak.asyncLock(); - + // Do some other work std::cout << "Doing other work while lock is in progress..." << std::endl; std::this_thread::sleep_for(100ms); - + // Get the result of the async lock auto asyncLocked = future.get(); if (asyncLocked) { @@ -255,31 +255,31 @@ void asynchronousOperationsExample() { } else { std::cout << "Async lock failed" << std::endl; } - + printSubSection("Waiting with Timeout"); // Set up a condition to wait for std::atomic condition{false}; - + // Start a thread that will set the condition after a delay std::thread conditionThread([&]() { std::this_thread::sleep_for(300ms); std::cout << "Setting condition to true" << std::endl; condition.store(true); }); - + // Wait for the object with timeout bool waitResult = weak.waitFor(500ms); std::cout << "waitFor result: " << (waitResult ? "Object available" : "Timeout or object expired") << std::endl; - + // Wait until the condition is true bool predResult = weak.waitUntil([&]() { return condition.load(); }); std::cout << "waitUntil result: " << (predResult ? "Condition met and object available" : "Object expired") << std::endl; - + // Cleanup if (conditionThread.joinable()) { conditionThread.join(); } - + printSubSection("Notification Mechanism"); // Create a thread that waits for notification std::atomic notified{false}; @@ -289,36 +289,36 @@ void asynchronousOperationsExample() { notified.store(true); std::cout << "Thread received notification or timed out" << std::endl; }); - + // Sleep briefly to ensure the thread starts waiting std::this_thread::sleep_for(100ms); - + // Send notification std::cout << "Sending notification to waiting threads..." << std::endl; weak.notifyAll(); - + // Join the thread if (waitingThread.joinable()) { waitingThread.join(); } - + std::cout << "Was thread notified? " << (notified.load() ? "Yes" : "No") << std::endl; } // Example 4: Type Casting and Special Operations void typeCastingExample() { printSection("Type Casting and Special Operations"); - + // Create a shared_ptr to a DerivedObject auto derivedShared = std::make_shared(5, "Derived Test", 3.14159); - + // Create an EnhancedWeakPtr to the base type EnhancedWeakPtr baseWeak(derivedShared); - + printSubSection("Type Casting"); // Cast the weak pointer to the derived type auto derivedWeak = baseWeak.cast(); - + // Test if the cast worked auto result = derivedWeak.withLock([](DerivedObject& obj) { std::cout << "Successfully cast to derived type" << std::endl; @@ -326,18 +326,18 @@ void typeCastingExample() { std::cout << "Derived property - Extra data: " << obj.getExtraData() << std::endl; return obj.getExtraData(); }); - + if (result) { std::cout << "Cast and lock succeeded, extra data value: " << *result << std::endl; } else { std::cout << "Cast or lock failed" << std::endl; } - + printSubSection("Weak Pointer to Shared Pointer"); // Get the underlying weak_ptr std::weak_ptr stdWeakPtr = baseWeak.getWeakPtr(); std::cout << "Standard weak_ptr use count: " << stdWeakPtr.use_count() << std::endl; - + // Create a shared_ptr from the weak_ptr auto createdShared = baseWeak.createShared(); if (createdShared) { @@ -346,35 +346,35 @@ void typeCastingExample() { } else { std::cout << "Failed to create shared_ptr (object expired)" << std::endl; } - + printSubSection("Total Instances Tracking"); // Get the total number of EnhancedWeakPtr instances size_t beforeCount = EnhancedWeakPtr::getTotalInstances(); std::cout << "Total EnhancedWeakPtr instances before: " << beforeCount << std::endl; - + // Create more instances { EnhancedWeakPtr temp1(derivedShared); EnhancedWeakPtr temp2(derivedShared); - + size_t duringCount = EnhancedWeakPtr::getTotalInstances(); std::cout << "Total EnhancedWeakPtr instances during: " << duringCount << std::endl; assert(duringCount > beforeCount); } - + size_t afterCount = EnhancedWeakPtr::getTotalInstances(); std::cout << "Total EnhancedWeakPtr instances after: " << afterCount << std::endl; assert(afterCount == beforeCount); - + printSubSection("Equality Comparison"); // Create two weak pointers to the same object EnhancedWeakPtr weak1(derivedShared); EnhancedWeakPtr weak2(derivedShared); - + // Create a weak pointer to a different object auto differentShared = std::make_shared(6, "Different Test"); EnhancedWeakPtr weak3(differentShared); - + // Compare weak pointers std::cout << "weak1 == weak2: " << (weak1 == weak2 ? "true" : "false") << std::endl; std::cout << "weak1 == weak3: " << (weak1 == weak3 ? "true" : "false") << std::endl; @@ -383,47 +383,47 @@ void typeCastingExample() { // Example 5: Void Specialization void voidSpecializationExample() { printSection("Void Specialization"); - + // Create a shared_ptr from a concrete type auto original = std::make_shared(7, "Void Test"); std::shared_ptr voidShared = original; - + // Create an EnhancedWeakPtr from the shared_ptr EnhancedWeakPtr voidWeak(voidShared); - + printSubSection("Basic Operations with void Type"); // Check if the weak pointer is expired std::cout << "Is void weak pointer expired? " << (voidWeak.expired() ? "Yes" : "No") << std::endl; - + // Get the use count std::cout << "Use count: " << voidWeak.useCount() << std::endl; - + // Lock the void weak pointer if (auto locked = voidWeak.lock()) { std::cout << "Successfully locked void weak pointer" << std::endl; } else { std::cout << "Failed to lock void weak pointer" << std::endl; } - + printSubSection("withLock for void Type"); // Use withLock with void return type bool success = voidWeak.withLock([]() { std::cout << "Performing void operation on void pointer" << std::endl; }); - + std::cout << "Void operation success: " << (success ? "Yes" : "No") << std::endl; - + // Use withLock with non-void return type auto result = voidWeak.withLock([]() { return std::string("Data from void pointer operation"); }); - + if (result) { std::cout << "withLock on void pointer returned: " << *result << std::endl; } else { std::cout << "withLock on void pointer failed" << std::endl; } - + printSubSection("tryLockOrElse with void Type"); // Use tryLockOrElse with void pointer auto resultOrDefault = voidWeak.tryLockOrElse( @@ -436,27 +436,27 @@ void voidSpecializationExample() { return "Failed to access void pointer"; } ); - + std::cout << "tryLockOrElse result: " << resultOrDefault << std::endl; - + printSubSection("Casting from void Type"); // Cast the void weak pointer back to the original type auto castBack = voidWeak.cast(); - + // Use withLock on the cast pointer auto name = castBack.withLock([](TestObject& obj) { return obj.getName(); }); - + if (name) { std::cout << "Successfully cast back from void to TestObject: " << *name << std::endl; } else { std::cout << "Failed to cast back from void to TestObject" << std::endl; } - + // Clean up original.reset(); - + // Verify both weak pointers are now expired std::cout << "Original weak ptr expired: " << (voidWeak.expired() ? "Yes" : "No") << std::endl; std::cout << "Cast weak ptr expired: " << (castBack.expired() ? "Yes" : "No") << std::endl; @@ -465,19 +465,19 @@ void voidSpecializationExample() { // Example 6: Group Operations void groupOperationsExample() { printSection("Group Operations"); - + // Create a vector of shared pointers std::vector> sharedPtrs; for (int i = 0; i < 5; ++i) { sharedPtrs.push_back(std::make_shared( 100 + i, "Group-" + std::to_string(i))); } - + printSubSection("Creating Weak Pointer Group"); // Create a group of weak pointers auto weakPtrGroup = createWeakPtrGroup(sharedPtrs); std::cout << "Created weak pointer group with " << weakPtrGroup.size() << " elements" << std::endl; - + printSubSection("Batch Operations"); // Perform a batch operation on the group std::cout << "Performing batch operation on the group..." << std::endl; @@ -485,136 +485,136 @@ void groupOperationsExample() { std::cout << "Batch operation on object #" << obj.getId() << " - " << obj.getName() << std::endl; obj.performOperation(); }); - + printSubSection("Individual Access After Batch"); // Access individual elements after batch operation for (size_t i = 0; i < weakPtrGroup.size(); ++i) { weakPtrGroup[i].withLock([i](TestObject& obj) { - std::cout << "Element " << i << " - ID: " << obj.getId() - << ", Name: " << obj.getName() + std::cout << "Element " << i << " - ID: " << obj.getId() + << ", Name: " << obj.getName() << ", Access count: " << obj.getAccessCount() << std::endl; }); } - + printSubSection("Handling Expired Group Members"); // Make some of the shared pointers expire std::cout << "Expiring elements 1 and 3..." << std::endl; sharedPtrs[1].reset(); sharedPtrs[3].reset(); - + // Try to access all elements including expired ones std::cout << "Trying to access all elements after expiration:" << std::endl; for (size_t i = 0; i < weakPtrGroup.size(); ++i) { bool accessed = weakPtrGroup[i].withLock([i](TestObject& obj) { - std::cout << "Element " << i << " - Successfully accessed object #" + std::cout << "Element " << i << " - Successfully accessed object #" << obj.getId() << std::endl; }); - + if (!accessed) { std::cout << "Element " << i << " - Failed to access (expired)" << std::endl; } } - + printSubSection("Batch Operation with Expiry Handling"); // Perform another batch operation with explicit handling std::cout << "Performing batch operation with expiry checks:" << std::endl; - + size_t successCount = 0; for (const auto& weakPtr : weakPtrGroup) { bool success = weakPtr.withLock([](TestObject& obj) { std::cout << "Processing object #" << obj.getId() << std::endl; obj.performOperation(); }); - + if (success) { successCount++; } } - - std::cout << "Successfully processed " << successCount << " out of " + + std::cout << "Successfully processed " << successCount << " out of " << weakPtrGroup.size() << " objects" << std::endl; } // Example 7: Multi-threading Scenarios void multiThreadingExample() { printSection("Multi-threading Scenarios"); - + // Create a shared pointer that will be accessed from multiple threads auto shared = std::make_shared(200, "Thread-Test"); EnhancedWeakPtr weak(shared); - + printSubSection("Concurrent Access"); // Set up a flag for coordination std::atomic shouldContinue{true}; - + // Track the total operations performed std::atomic totalOperations{0}; - + // Start multiple reader threads std::vector threads; for (int i = 0; i < 5; ++i) { threads.emplace_back([&weak, &shouldContinue, &totalOperations, i]() { std::cout << "Thread " << i << " started" << std::endl; int localCount = 0; - + while (shouldContinue.load()) { // Try to access the object weak.withLock([i, &localCount](TestObject& obj) { localCount++; - std::cout << "Thread " << i << " accessing object #" + std::cout << "Thread " << i << " accessing object #" << obj.getId() << ", local count: " << localCount << std::endl; - + // Simulate some work std::this_thread::sleep_for(50ms); }); - + // Small delay between attempts std::this_thread::sleep_for(20ms); } - + // Update the total count totalOperations.fetch_add(localCount); std::cout << "Thread " << i << " finished, local operations: " << localCount << std::endl; }); } - + // Let the threads run for a while std::this_thread::sleep_for(500ms); - + printSubSection("Object Expiration During Thread Execution"); // Reset the shared pointer while threads are running std::cout << "Resetting shared pointer while threads are accessing it..." << std::endl; shared.reset(); - + // Let the threads continue for a bit after expiration std::this_thread::sleep_for(300ms); - + // Signal threads to stop std::cout << "Signaling threads to stop..." << std::endl; shouldContinue.store(false); - + // Wait for all threads to complete for (auto& t : threads) { if (t.joinable()) { t.join(); } } - + std::cout << "All threads completed. Total operations: " << totalOperations.load() << std::endl; std::cout << "Lock attempts recorded: " << weak.getLockAttempts() << std::endl; - + printSubSection("Coordination with Condition Variables"); // Create a new shared pointer shared = std::make_shared(201, "CV-Test"); EnhancedWeakPtr cvWeak(shared); - + // Create a waiter thread std::thread waiterThread([&cvWeak]() { std::cout << "Waiter thread waiting for object to become available..." << std::endl; bool success = cvWeak.waitFor(2s); std::cout << "Waiter thread done. Object available: " << (success ? "Yes" : "No") << std::endl; }); - + // Create a notifier thread std::thread notifierThread([&cvWeak]() { std::cout << "Notifier thread sleeping before notification..." << std::endl; @@ -622,7 +622,7 @@ void multiThreadingExample() { std::cout << "Notifier thread sending notification..." << std::endl; cvWeak.notifyAll(); }); - + // Wait for threads to complete if (waiterThread.joinable()) waiterThread.join(); if (notifierThread.joinable()) notifierThread.join(); @@ -631,25 +631,25 @@ void multiThreadingExample() { // Example 8: Error Handling and Edge Cases void errorHandlingExample() { printSection("Error Handling and Edge Cases"); - + printSubSection("Construction and Assignment"); // Default construction EnhancedWeakPtr defaultWeak; std::cout << "Default constructed weak ptr expired: " << (defaultWeak.expired() ? "Yes" : "No") << std::endl; - + // Construction from nullptr or empty shared_ptr std::shared_ptr nullShared; EnhancedWeakPtr nullWeak(nullShared); std::cout << "Null constructed weak ptr expired: " << (nullWeak.expired() ? "Yes" : "No") << std::endl; - + // Copy construction EnhancedWeakPtr copyWeak = nullWeak; std::cout << "Copy constructed weak ptr expired: " << (copyWeak.expired() ? "Yes" : "No") << std::endl; - + // Move construction EnhancedWeakPtr moveWeak = std::move(copyWeak); std::cout << "Move constructed weak ptr expired: " << (moveWeak.expired() ? "Yes" : "No") << std::endl; - + printSubSection("Edge Cases in Locking"); // Create temporary object then let it expire EnhancedWeakPtr tempWeak; @@ -659,23 +659,23 @@ void errorHandlingExample() { std::cout << "Temporary weak ptr expired (inside scope): " << (tempWeak.expired() ? "Yes" : "No") << std::endl; } std::cout << "Temporary weak ptr expired (outside scope): " << (tempWeak.expired() ? "Yes" : "No") << std::endl; - + // Try to lock expired pointer auto locked = tempWeak.lock(); std::cout << "Lock result on expired pointer: " << (locked ? "Succeeded (unexpected)" : "Failed (expected)") << std::endl; - + // Try to use withLock on expired pointer bool success = tempWeak.withLock([](TestObject& obj) { std::cout << "This should not print" << std::endl; }); std::cout << "withLock on expired pointer: " << (success ? "Succeeded (unexpected)" : "Failed (expected)") << std::endl; - + printSubSection("Validation in Boost Mode"); #ifdef ATOM_USE_BOOST // Create a valid pointer auto validShared = std::make_shared(301, "Valid"); EnhancedWeakPtr validWeak(validShared); - + try { std::cout << "Validating valid pointer..." << std::endl; validWeak.validate(); @@ -683,10 +683,10 @@ void errorHandlingExample() { } catch (const EnhancedWeakPtrException& e) { std::cout << "Unexpected exception: " << e.what() << std::endl; } - + // Make it expire validShared.reset(); - + try { std::cout << "Validating expired pointer..." << std::endl; validWeak.validate(); @@ -702,32 +702,32 @@ void errorHandlingExample() { // Create an object that will be contested auto contestedShared = std::make_shared(302, "Contested"); EnhancedWeakPtr contestedWeak(contestedShared); - + // Create multiple threads that will race to access and possibly reset std::vector racingThreads; std::atomic successfulAccesses{0}; std::atomic failedAccesses{0}; - + // This will be set by one of the threads std::atomic hasReset{false}; - + for (int i = 0; i < 10; ++i) { racingThreads.emplace_back([&contestedWeak, &successfulAccesses, &failedAccesses, &hasReset, i]() { // Random delay to increase chance of race conditions std::this_thread::sleep_for(std::chrono::milliseconds(rand() % 50)); - + // Thread 5 will reset the pointer if (i == 5 && !hasReset.load()) { hasReset.store(true); std::cout << "Thread " << i << " resetting weak pointer" << std::endl; contestedWeak.reset(); } - + // All threads try to access bool success = contestedWeak.withLock([i](TestObject& obj) { std::cout << "Thread " << i << " successfully accessed object #" << obj.getId() << std::endl; }); - + if (success) { successfulAccesses++; } else { @@ -736,14 +736,14 @@ void errorHandlingExample() { } }); } - + // Wait for all threads to complete for (auto& t : racingThreads) { if (t.joinable()) { t.join(); } } - + std::cout << "Race condition test completed." << std::endl; std::cout << "Successful accesses: " << successfulAccesses.load() << std::endl; std::cout << "Failed accesses: " << failedAccesses.load() << std::endl; @@ -753,7 +753,7 @@ int main() { std::cout << "===============================================" << std::endl; std::cout << " EnhancedWeakPtr Comprehensive Examples " << std::endl; std::cout << "===============================================" << std::endl; - + // Run all examples try { basicUsageExample(); @@ -764,12 +764,12 @@ int main() { groupOperationsExample(); multiThreadingExample(); errorHandlingExample(); - + std::cout << "\nAll examples completed successfully!" << std::endl; } catch (const std::exception& e) { std::cerr << "An unexpected error occurred: " << e.what() << std::endl; return 1; } - + return 0; -} \ No newline at end of file +} diff --git a/example/utils/CMakeLists.txt b/example/utils/CMakeLists.txt index 8002b1cc..a576e571 100644 --- a/example/utils/CMakeLists.txt +++ b/example/utils/CMakeLists.txt @@ -14,20 +14,20 @@ file(GLOB CPP_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) foreach(CPP_FILE ${CPP_FILES}) # 获取不带扩展名的文件名 get_filename_component(EXAMPLE_NAME ${CPP_FILE} NAME_WE) - + # 构造可执行文件名称(子目录名_文件名) set(EXECUTABLE_NAME ${SUBDIR_NAME}_${EXAMPLE_NAME}) - + # 配置选项,允许单独控制每个示例的构建 string(TOUPPER ${EXAMPLE_NAME} EXAMPLE_NAME_UPPER) option(ATOM_EXAMPLE_UTILS_${EXAMPLE_NAME_UPPER} "Build utils example: ${EXAMPLE_NAME}" ${ATOM_EXAMPLE_UTILS_BUILD_ALL}) - + # 有条件地添加可执行文件 if(ATOM_EXAMPLE_UTILS_${EXAMPLE_NAME_UPPER}) add_executable(${EXECUTABLE_NAME} ${CPP_FILE}) target_link_libraries(${EXECUTABLE_NAME} atom) - - + + # 设置IDE文件夹分组 set_property(TARGET ${EXECUTABLE_NAME} PROPERTY FOLDER "Examples/Utils") endif() diff --git a/example/utils/aes.cpp b/example/utils/aes.cpp index bd212a17..379b41bd 100644 --- a/example/utils/aes.cpp +++ b/example/utils/aes.cpp @@ -533,4 +533,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/utils/aligned.cpp b/example/utils/aligned.cpp index 06e209af..b2dede70 100644 --- a/example/utils/aligned.cpp +++ b/example/utils/aligned.cpp @@ -427,4 +427,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/utils/anyutils.cpp b/example/utils/anyutils.cpp index a22112b6..ba13c17b 100644 --- a/example/utils/anyutils.cpp +++ b/example/utils/anyutils.cpp @@ -586,4 +586,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/utils/argsview.cpp b/example/utils/argsview.cpp index 350b6180..b9b82e8c 100644 --- a/example/utils/argsview.cpp +++ b/example/utils/argsview.cpp @@ -577,4 +577,4 @@ int main(int argc, char* argv[]) { } return 0; -} \ No newline at end of file +} diff --git a/example/utils/bit.cpp b/example/utils/bit.cpp index e15f3b3c..c3cb43cd 100644 --- a/example/utils/bit.cpp +++ b/example/utils/bit.cpp @@ -430,4 +430,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/utils/container.cpp b/example/utils/container.cpp index a241405a..f1d265e6 100644 --- a/example/utils/container.cpp +++ b/example/utils/container.cpp @@ -21,14 +21,14 @@ #include #include #include - + // Helper function to print section headers void printSection(const std::string& title) { std::cout << "\n===============================================" << std::endl; std::cout << " " << title << std::endl; std::cout << "===============================================" << std::endl; } - + // Helper function to print containers template void printContainer(const std::string& label, const Container& container) { @@ -41,7 +41,7 @@ } std::cout << "]" << std::endl; } - + // Helper function to print pairs template void printPairs(const std::string& label, const std::vector>& pairs) { @@ -54,7 +54,7 @@ } std::cout << "]" << std::endl; } - + // Helper function to print maps template void printMap(const std::string& label, const std::map& map) { @@ -67,34 +67,34 @@ } std::cout << "}" << std::endl; } - + // Sample class to demonstrate member function handling class Person { public: Person(std::string name, int age, std::string city) : name_(std::move(name)), age_(age), city_(std::move(city)) {} - + std::string getName() const { return name_; } int getAge() const { return age_; } std::string getCity() const { return city_; } - + // For printing Person objects friend std::ostream& operator<<(std::ostream& os, const Person& person) { os << person.name_ << "(" << person.age_ << ")"; return os; } - + // For making Person objects hashable bool operator==(const Person& other) const { return name_ == other.name_ && age_ == other.age_ && city_ == other.city_; } - + private: std::string name_; int age_; std::string city_; }; - + // Make Person hashable for std::unordered_set namespace std { template<> @@ -104,30 +104,30 @@ } }; } - + int main() { try { std::cout << "Container Utilities Demonstration" << std::endl; - + // =================================================== // Example 1: Basic Container Operations and Subset Checking // =================================================== printSection("1. Basic Container Operations and Subset Checking"); - + // Create different container types std::vector vec1 = {1, 2, 3, 4, 5}; std::list list1 = {2, 3, 4}; std::set set1 = {3, 4, 5, 6, 7}; - + printContainer("Vector", vec1); printContainer("List", list1); printContainer("Set", set1); - + // Test contains function std::cout << "\nContains function demonstration:" << std::endl; std::cout << "Vector contains 3: " << (atom::utils::contains(vec1, 3) ? "Yes" : "No") << std::endl; std::cout << "Vector contains 8: " << (atom::utils::contains(vec1, 8) ? "Yes" : "No") << std::endl; - + // Test conversion to unordered_set std::cout << "\nToUnorderedSet demonstration:" << std::endl; auto vec1AsSet = atom::utils::toUnorderedSet(vec1); @@ -135,73 +135,73 @@ std::cout << "Checking membership in unordered_set:" << std::endl; std::cout << "Contains 3: " << (vec1AsSet.contains(3) ? "Yes" : "No") << std::endl; std::cout << "Contains 8: " << (vec1AsSet.contains(8) ? "Yes" : "No") << std::endl; - + // Test subset operations with different algorithms std::cout << "\nSubset checking demonstration:" << std::endl; - std::cout << "Is list a subset of vector (isSubset): " + std::cout << "Is list a subset of vector (isSubset): " << (atom::utils::isSubset(list1, vec1) ? "Yes" : "No") << std::endl; - std::cout << "Is list a subset of vector (linearSearch): " + std::cout << "Is list a subset of vector (linearSearch): " << (atom::utils::isSubsetLinearSearch(list1, vec1) ? "Yes" : "No") << std::endl; - std::cout << "Is list a subset of vector (hashSet): " + std::cout << "Is list a subset of vector (hashSet): " << (atom::utils::isSubsetWithHashSet(list1, vec1) ? "Yes" : "No") << std::endl; - + // Test negative subset case std::list list2 = {2, 3, 8}; printContainer("List 2", list2); - std::cout << "Is list2 a subset of vector: " + std::cout << "Is list2 a subset of vector: " << (atom::utils::isSubset(list2, vec1) ? "Yes" : "No") << std::endl; - + // =================================================== // Example 2: Set Operations // =================================================== printSection("2. Set Operations"); - + // Create test containers std::vector setA = {1, 2, 3, 4, 5}; std::list setB = {4, 5, 6, 7}; - + printContainer("Set A", setA); printContainer("Set B", setB); - + // Test intersection auto intersect = atom::utils::intersection(setA, setB); printContainer("Intersection (A ∩ B)", intersect); - + // Test union auto unionSet = atom::utils::unionSet(setA, setB); printContainer("Union (A ∪ B)", unionSet); - + // Test difference auto diff1 = atom::utils::difference(setA, setB); printContainer("Difference (A - B)", diff1); - + auto diff2 = atom::utils::difference(setB, setA); printContainer("Difference (B - A)", diff2); - + // Test symmetric difference auto symDiff = atom::utils::symmetricDifference(setA, setB); printContainer("Symmetric Difference", symDiff); - + // Test container equality std::vector vecEqual1 = {1, 2, 3}; std::list listEqual1 = {1, 2, 3}; std::set setEqual1 = {3, 2, 1}; // Different order but same elements - + printContainer("Vector for equality", vecEqual1); printContainer("List for equality", listEqual1); printContainer("Set for equality", setEqual1); - + std::cout << "\nEquality checking demonstration:" << std::endl; - std::cout << "Vector equals List: " + std::cout << "Vector equals List: " << (atom::utils::isEqual(vecEqual1, listEqual1) ? "Yes" : "No") << std::endl; - std::cout << "Vector equals Set: " + std::cout << "Vector equals Set: " << (atom::utils::isEqual(vecEqual1, setEqual1) ? "Yes" : "No") << std::endl; - + // =================================================== // Example 3: Container Transformations // =================================================== printSection("3. Container Transformations"); - + // Create a vector of Person objects std::vector people = { Person("Alice", 30, "New York"), @@ -209,40 +209,40 @@ Person("Charlie", 35, "Los Angeles"), Person("David", 28, "Boston") }; - + std::cout << "People collection:" << std::endl; for (const auto& person : people) { - std::cout << " " << person.getName() << ", Age: " << person.getAge() + std::cout << " " << person.getName() << ", Age: " << person.getAge() << ", City: " << person.getCity() << std::endl; } - + // Transform container using member functions std::cout << "\nTransforming containers using member functions:" << std::endl; - + auto names = atom::utils::transformToVector(people, &Person::getName); printContainer("Names", names); - + auto ages = atom::utils::transformToVector(people, &Person::getAge); printContainer("Ages", ages); - + auto cities = atom::utils::transformToVector(people, &Person::getCity); printContainer("Cities", cities); - + // Test applyAndStore (alternative transformation function) std::cout << "\nUsing applyAndStore function:" << std::endl; auto namesByApply = atom::utils::applyAndStore(people, &Person::getName); printContainer("Names by applyAndStore", namesByApply); - + // =================================================== // Example 4: Handling Duplicates // =================================================== printSection("4. Handling Duplicates"); - + // Create containers with duplicates std::vector duplicateInts = {1, 2, 2, 3, 4, 4, 5, 5, 5}; std::vector duplicateStrings = {"apple", "banana", "apple", "cherry", "banana", "date"}; std::map duplicateMap = {{"a", 1}, {"b", 2}, {"a", 3}, {"c", 4}}; - + printContainer("Duplicate Integers", duplicateInts); printContainer("Duplicate Strings", duplicateStrings); std::cout << "Duplicate Map entries: "; @@ -250,12 +250,12 @@ std::cout << key << ":" << value << " "; } std::cout << std::endl; - + // Remove duplicates auto uniqueInts = atom::utils::unique(duplicateInts); auto uniqueStrings = atom::utils::unique(duplicateStrings); auto uniqueMap = atom::utils::unique(duplicateMap); - + printContainer("Unique Integers", uniqueInts); printContainer("Unique Strings", uniqueStrings); std::cout << "Unique Map entries: "; @@ -263,173 +263,173 @@ std::cout << key << ":" << value << " "; } std::cout << std::endl; - + // =================================================== // Example 5: Container Flattening // =================================================== printSection("5. Container Flattening"); - + // Create nested containers std::vector> nestedInts = { {1, 2, 3}, {4, 5}, {6, 7, 8, 9} }; - + std::cout << "Nested integers:" << std::endl; for (const auto& innerVec : nestedInts) { printContainer(" Inner vector", innerVec); } - + // Flatten the nested containers auto flattenedInts = atom::utils::flatten(nestedInts); printContainer("Flattened integers", flattenedInts); - + // More complex example - nested lists std::vector> nestedLists = { {"red", "green", "blue"}, {"apple", "banana"}, {"one", "two", "three"} }; - + std::cout << "\nNested lists:" << std::endl; for (const auto& innerList : nestedLists) { printContainer(" Inner list", innerList); } - + // Flatten the nested lists auto flattenedStrings = atom::utils::flatten(nestedLists); printContainer("Flattened strings", flattenedStrings); - + // =================================================== // Example 6: Container Combining Operations // =================================================== printSection("6. Container Combining Operations"); - + // Create containers to combine std::vector letters = {'A', 'B', 'C'}; std::list numbers = {1, 2, 3, 4, 5}; // Longer than letters - + printContainer("Letters", letters); printContainer("Numbers", numbers); - + // Zip containers std::cout << "\nZip operation (combines corresponding elements):" << std::endl; auto zipped = atom::utils::zip(letters, numbers); printPairs("Zipped pairs", zipped); std::cout << "Note: Zip stops at the end of the shortest container" << std::endl; - + // Cartesian product std::cout << "\nCartesian product (all possible combinations):" << std::endl; auto product = atom::utils::cartesianProduct(letters, std::vector{1, 2}); printPairs("Cartesian product", product); - + // =================================================== // Example 7: Filtering and Partitioning // =================================================== printSection("7. Filtering and Partitioning"); - + // Create a container to filter std::vector mixedNumbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; printContainer("Mixed Numbers", mixedNumbers); - + // Define predicates auto isEven = [](int n) { return n % 2 == 0; }; auto isGreaterThan5 = [](int n) { return n > 5; }; - + // Filter elements std::cout << "\nFiltering demonstration:" << std::endl; auto evenNumbers = atom::utils::filter(mixedNumbers, isEven); printContainer("Even numbers", evenNumbers); - + auto largeNumbers = atom::utils::filter(mixedNumbers, isGreaterThan5); printContainer("Numbers > 5", largeNumbers); - + // Partition elements std::cout << "\nPartitioning demonstration:" << std::endl; auto [even, odd] = atom::utils::partition(mixedNumbers, isEven); printContainer("Even partition", even); printContainer("Odd partition", odd); - + auto [large, small] = atom::utils::partition(mixedNumbers, isGreaterThan5); printContainer("Large partition (>5)", large); printContainer("Small partition (≤5)", small); - + // =================================================== // Example 8: Finding Elements // =================================================== printSection("8. Finding Elements"); - + std::vector employees = { Person("John", 42, "Seattle"), Person("Sarah", 38, "Portland"), Person("Michael", 29, "San Francisco"), Person("Emma", 45, "Seattle") }; - + std::cout << "Employee collection:" << std::endl; for (const auto& employee : employees) { - std::cout << " " << employee.getName() << ", Age: " << employee.getAge() + std::cout << " " << employee.getName() << ", Age: " << employee.getAge() << ", City: " << employee.getCity() << std::endl; } - + // Find first element that satisfies predicate std::cout << "\nFinding elements demonstration:" << std::endl; - - auto youngEmployee = atom::utils::findIf(employees, [](const Person& p) { - return p.getAge() < 30; + + auto youngEmployee = atom::utils::findIf(employees, [](const Person& p) { + return p.getAge() < 30; }); - + if (youngEmployee) { - std::cout << "Found young employee: " << youngEmployee->getName() + std::cout << "Found young employee: " << youngEmployee->getName() << ", Age: " << youngEmployee->getAge() << std::endl; } else { std::cout << "No young employee found" << std::endl; } - - auto seattleEmployee = atom::utils::findIf(employees, [](const Person& p) { - return p.getCity() == "Seattle"; + + auto seattleEmployee = atom::utils::findIf(employees, [](const Person& p) { + return p.getCity() == "Seattle"; }); - + if (seattleEmployee) { - std::cout << "Found Seattle employee: " << seattleEmployee->getName() + std::cout << "Found Seattle employee: " << seattleEmployee->getName() << ", Age: " << seattleEmployee->getAge() << std::endl; } else { std::cout << "No Seattle employee found" << std::endl; } - - auto oldEmployee = atom::utils::findIf(employees, [](const Person& p) { - return p.getAge() > 50; + + auto oldEmployee = atom::utils::findIf(employees, [](const Person& p) { + return p.getAge() > 50; }); - + if (oldEmployee) { - std::cout << "Found employee over 50: " << oldEmployee->getName() + std::cout << "Found employee over 50: " << oldEmployee->getName() << ", Age: " << oldEmployee->getAge() << std::endl; } else { std::cout << "No employee over 50 found" << std::endl; } - + // =================================================== // Example 9: String Literal to Vector // =================================================== printSection("9. String Literal to Vector"); - + // Use the custom string literal operator auto fruits = "apple, banana, cherry, date"_vec; printContainer("Fruits from string literal", fruits); - + auto colors = "red,green,blue,yellow"_vec; printContainer("Colors from string literal", colors); - + auto mixedSpacing = " item1 ,item2, item3,item4 "_vec; printContainer("Mixed spacing from string literal", mixedSpacing); - + std::cout << "\nAll examples completed successfully!" << std::endl; - + } catch (const std::exception& e) { std::cerr << "ERROR: " << e.what() << std::endl; return 1; } - + return 0; - } \ No newline at end of file + } diff --git a/example/utils/convert.cpp b/example/utils/convert.cpp index 0d0089fd..669a1ac9 100644 --- a/example/utils/convert.cpp +++ b/example/utils/convert.cpp @@ -67,4 +67,4 @@ int main() { return 0; } -#endif \ No newline at end of file +#endif diff --git a/example/utils/cstring.cpp b/example/utils/cstring.cpp index 9a17edf0..e696219a 100644 --- a/example/utils/cstring.cpp +++ b/example/utils/cstring.cpp @@ -450,4 +450,4 @@ int main() { std::cout << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/utils/difflib.cpp b/example/utils/difflib.cpp index 4fef1131..775389e9 100644 --- a/example/utils/difflib.cpp +++ b/example/utils/difflib.cpp @@ -1,7 +1,7 @@ /** * @file difflib_example.cpp * @brief Comprehensive examples demonstrating difflib utilities - * + * * This example demonstrates all functions available in atom::utils::difflib.hpp: * - SequenceMatcher for comparing sequences * - Differ for generating text differences @@ -17,48 +17,48 @@ #include #include #include - + // Helper function to print section headers void printSection(const std::string& title) { std::cout << "\n===============================================" << std::endl; std::cout << " " << title << std::endl; std::cout << "===============================================" << std::endl; } - + // Helper function to print sequences void printSequences(const std::vector& seq1, const std::vector& seq2) { std::cout << "Sequence 1:" << std::endl; for (const auto& item : seq1) { std::cout << " " << item << std::endl; } - + std::cout << "\nSequence 2:" << std::endl; for (const auto& item : seq2) { std::cout << " " << item << std::endl; } std::cout << std::endl; } - + // Helper function to print the matching blocks void printMatchingBlocks(const std::vector>& blocks) { std::cout << "Matching blocks:" << std::endl; for (const auto& [a, b, size] : blocks) { - std::cout << " a[" << a << ":" << (a + size) << "] == b[" << b << ":" + std::cout << " a[" << a << ":" << (a + size) << "] == b[" << b << ":" << (b + size) << "] (size: " << size << ")" << std::endl; } std::cout << std::endl; } - + // Helper function to print the opcodes void printOpcodes(const std::vector>& opcodes) { std::cout << "Opcodes:" << std::endl; for (const auto& [tag, i1, i2, j1, j2] : opcodes) { - std::cout << " " << std::left << std::setw(8) << tag + std::cout << " " << std::left << std::setw(8) << tag << " a[" << i1 << ":" << i2 << "] b[" << j1 << ":" << j2 << "]" << std::endl; } std::cout << std::endl; } - + // Helper function to save a string to a file bool saveToFile(const std::string& filename, const std::string& content) { std::ofstream file(filename); @@ -66,76 +66,76 @@ std::cerr << "Failed to open file for writing: " << filename << std::endl; return false; } - + file << content; file.close(); return true; } - + int main() { try { std::cout << "Difflib Utilities Demonstration" << std::endl; - + // =================================================== // Example 1: Basic String Comparison with SequenceMatcher // =================================================== printSection("1. Basic String Comparison with SequenceMatcher"); - + // Compare two simple strings std::string str1 = "This is the first test string."; std::string str2 = "This is the second test string."; - + std::cout << "String 1: \"" << str1 << "\"" << std::endl; std::cout << "String 2: \"" << str2 << "\"" << std::endl; - + // Create a SequenceMatcher atom::utils::SequenceMatcher matcher(str1, str2); - + // Calculate similarity ratio double similarity = matcher.ratio(); - std::cout << "Similarity ratio: " << similarity << " (" + std::cout << "Similarity ratio: " << similarity << " (" << static_cast(similarity * 100) << "%)" << std::endl; - + // Get matching blocks auto blocks = matcher.getMatchingBlocks(); printMatchingBlocks(blocks); - + // Get opcodes auto opcodes = matcher.getOpcodes(); printOpcodes(opcodes); - + // =================================================== // Example 2: Comparing Different Strings // =================================================== printSection("2. Comparing Different Strings"); - + // Compare two more different strings std::string text1 = "The quick brown fox jumps over the lazy dog."; std::string text2 = "A quick brown dog jumps over the lazy fox."; - + std::cout << "Text 1: \"" << text1 << "\"" << std::endl; std::cout << "Text 2: \"" << text2 << "\"" << std::endl; - + // Set new sequences to compare matcher.setSeqs(text1, text2); - + // Calculate similarity ratio similarity = matcher.ratio(); - std::cout << "Similarity ratio: " << similarity << " (" + std::cout << "Similarity ratio: " << similarity << " (" << static_cast(similarity * 100) << "%)" << std::endl; - + // Get matching blocks and opcodes blocks = matcher.getMatchingBlocks(); printMatchingBlocks(blocks); - + opcodes = matcher.getOpcodes(); printOpcodes(opcodes); - + // =================================================== // Example 3: Comparing Line Sequences with Differ // =================================================== printSection("3. Comparing Line Sequences with Differ"); - + // Create two sequences of lines std::vector lines1 = { "Line 1: This is a test.", @@ -144,7 +144,7 @@ "Line 4: This line will be removed.", "Line 5: The end." }; - + std::vector lines2 = { "Line 1: This is a test.", "Line 2: The quick brown fox jumps over the lazy cat.", // Changed dog -> cat @@ -152,58 +152,58 @@ "Line 5: The end.", // Line 4 removed "Line 6: An additional line." // New line added }; - + // Print the original sequences printSequences(lines1, lines2); - + // Generate differences using Differ std::cout << "Differences (Differ::compare):" << std::endl; auto diffs = atom::utils::Differ::compare(lines1, lines2); - + for (const auto& line : diffs) { std::cout << line << std::endl; } - + // =================================================== // Example 4: Unified Diff Format // =================================================== printSection("4. Unified Diff Format"); - + // Generate unified diff with default parameters std::cout << "Unified diff (default context=3):" << std::endl; auto unified_diff = atom::utils::Differ::unifiedDiff(lines1, lines2); - + for (const auto& line : unified_diff) { std::cout << line << std::endl; } - + // Generate unified diff with custom parameters std::cout << "\nUnified diff (custom labels, context=1):" << std::endl; auto custom_diff = atom::utils::Differ::unifiedDiff( lines1, lines2, "original.txt", "modified.txt", 1); - + for (const auto& line : custom_diff) { std::cout << line << std::endl; } - + // =================================================== // Example 5: HTML Diff Visualization // =================================================== printSection("5. HTML Diff Visualization"); - + // Generate HTML diff table std::cout << "Generating HTML diff table..." << std::endl; auto html_table_result = atom::utils::HtmlDiff::makeTable( lines1, lines2, "Original Text", "Modified Text"); - + if (html_table_result) { std::cout << "HTML table generated successfully." << std::endl; std::cout << "HTML table size: " << html_table_result->size() << " bytes" << std::endl; - + // Show first 200 characters std::cout << "Preview:" << std::endl; std::cout << html_table_result->substr(0, 200) << "..." << std::endl; - + // Save to file if (saveToFile("diff_table.html", *html_table_result)) { std::cout << "Saved to diff_table.html" << std::endl; @@ -211,16 +211,16 @@ } else { std::cerr << "Failed to generate HTML table: " << html_table_result.error() << std::endl; } - + // Generate complete HTML file std::cout << "\nGenerating complete HTML diff file..." << std::endl; auto html_file_result = atom::utils::HtmlDiff::makeFile( lines1, lines2, "Original Text", "Modified Text"); - + if (html_file_result) { std::cout << "HTML file generated successfully." << std::endl; std::cout << "HTML file size: " << html_file_result->size() << " bytes" << std::endl; - + // Save to file if (saveToFile("diff_complete.html", *html_file_result)) { std::cout << "Saved to diff_complete.html" << std::endl; @@ -228,12 +228,12 @@ } else { std::cerr << "Failed to generate HTML file: " << html_file_result.error() << std::endl; } - + // =================================================== // Example 6: Finding Close Matches // =================================================== printSection("6. Finding Close Matches"); - + // Define a list of words std::vector words = { "apple", "banana", "cherry", "date", "elderberry", @@ -241,7 +241,7 @@ "kiwi", "lemon", "mango", "nectarine", "orange", "papaya", "quince", "raspberry", "strawberry", "tangerine" }; - + std::cout << "List of words:" << std::endl; for (size_t i = 0; i < words.size(); ++i) { std::cout << words[i]; @@ -253,15 +253,15 @@ } } std::cout << std::endl; - + // Find close matches for slightly misspelled words std::vector test_words = { "aple", "strberry", "lemen", "banna", "grap" }; - + for (const auto& test_word : test_words) { std::cout << "Finding close matches for \"" << test_word << "\":" << std::endl; - + // Test with default parameters auto matches = atom::utils::getCloseMatches(test_word, words); std::cout << " Default (n=3, cutoff=0.6): "; @@ -272,7 +272,7 @@ } } std::cout << std::endl; - + // Test with different parameters auto matches2 = atom::utils::getCloseMatches(test_word, words, 1, 0.7); std::cout << " Custom (n=1, cutoff=0.7): "; @@ -280,7 +280,7 @@ std::cout << match; } std::cout << std::endl; - + // Test with very low cutoff auto matches3 = atom::utils::getCloseMatches(test_word, words, 5, 0.4); std::cout << " Custom (n=5, cutoff=0.4): "; @@ -292,22 +292,22 @@ } std::cout << std::endl; } - + // =================================================== // Example 7: Performance Testing with Larger Texts // =================================================== printSection("7. Performance Testing with Larger Texts"); - + // Generate larger text samples std::vector large_text1; std::vector large_text2; - + // Add some repeated content with variations for (int i = 0; i < 100; ++i) { std::ostringstream line1, line2; line1 << "Line " << i << ": This is test line number " << i << " in the first document."; large_text1.push_back(line1.str()); - + // Make some differences in the second document if (i % 10 == 0) { // Skip this line in text2 (deletion) @@ -329,102 +329,102 @@ large_text2.push_back(line2.str()); } } - + std::cout << "Created large text samples:" << std::endl; std::cout << " Text 1: " << large_text1.size() << " lines" << std::endl; std::cout << " Text 2: " << large_text2.size() << " lines" << std::endl; - + // Measure performance of different operations - + // 1. SequenceMatcher std::cout << "\nTesting SequenceMatcher performance..." << std::endl; auto start_time = std::chrono::high_resolution_clock::now(); - + std::string joined_text1; std::string joined_text2; - + for (const auto& line : large_text1) { joined_text1 += line + "\n"; } - + for (const auto& line : large_text2) { joined_text2 += line + "\n"; } - + atom::utils::SequenceMatcher large_matcher(joined_text1, joined_text2); double large_similarity = large_matcher.ratio(); - + auto end_time = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast(end_time - start_time).count(); - + std::cout << " Similarity ratio: " << large_similarity << std::endl; std::cout << " Time taken: " << duration << " ms" << std::endl; - + // 2. Differ::compare std::cout << "\nTesting Differ::compare performance..." << std::endl; start_time = std::chrono::high_resolution_clock::now(); - + auto large_diffs = atom::utils::Differ::compare(large_text1, large_text2); - + end_time = std::chrono::high_resolution_clock::now(); duration = std::chrono::duration_cast(end_time - start_time).count(); - + std::cout << " Generated diff with " << large_diffs.size() << " lines" << std::endl; std::cout << " Time taken: " << duration << " ms" << std::endl; - + // 3. HtmlDiff::makeTable std::cout << "\nTesting HtmlDiff::makeTable performance..." << std::endl; start_time = std::chrono::high_resolution_clock::now(); - + auto large_html_table = atom::utils::HtmlDiff::makeTable(large_text1, large_text2); - + end_time = std::chrono::high_resolution_clock::now(); duration = std::chrono::duration_cast(end_time - start_time).count(); - + if (large_html_table) { std::cout << " Generated HTML table with " << large_html_table->size() << " bytes" << std::endl; } else { std::cout << " Failed to generate HTML table: " << large_html_table.error() << std::endl; } std::cout << " Time taken: " << duration << " ms" << std::endl; - + // =================================================== // Example 8: Edge Cases and Special Scenarios // =================================================== printSection("8. Edge Cases and Special Scenarios"); - + // Case 1: Empty strings std::cout << "Comparing empty strings:" << std::endl; atom::utils::SequenceMatcher empty_matcher("", ""); std::cout << " Similarity ratio: " << empty_matcher.ratio() << std::endl; - + // Case 2: Empty vs non-empty std::cout << "\nComparing empty vs non-empty string:" << std::endl; atom::utils::SequenceMatcher mixed_matcher("", "Hello world"); std::cout << " Similarity ratio: " << mixed_matcher.ratio() << std::endl; - + // Case 3: Identical strings std::cout << "\nComparing identical strings:" << std::endl; std::string identical = "This string is exactly the same in both cases."; atom::utils::SequenceMatcher identical_matcher(identical, identical); std::cout << " Similarity ratio: " << identical_matcher.ratio() << std::endl; - + // Case 4: Finding close matches with empty string std::cout << "\nFinding close matches for empty string:" << std::endl; auto empty_matches = atom::utils::getCloseMatches("", words); std::cout << " Found " << empty_matches.size() << " matches" << std::endl; - + // Case 5: Finding close matches in empty list std::cout << "\nFinding close matches in empty list:" << std::endl; std::vector empty_list; auto no_matches = atom::utils::getCloseMatches("apple", empty_list); std::cout << " Found " << no_matches.size() << " matches" << std::endl; - + // =================================================== // Example 9: Practical Application - Spell Checker // =================================================== printSection("9. Practical Application - Simple Spell Checker"); - + // Define a dictionary of correctly spelled words std::vector dictionary = { "algorithm", "application", "binary", "compiler", "computer", @@ -433,17 +433,17 @@ "network", "operating", "processor", "programming", "recursive", "software", "storage", "structure", "system", "variable" }; - + // Define some misspelled words to check std::vector misspelled_words = { "algorthm", "aplicasion", "compiller", "developmint", "recursve" }; - + std::cout << "Simple spell checker:" << std::endl; for (const auto& word : misspelled_words) { std::cout << "Checking \"" << word << "\":" << std::endl; auto suggestions = atom::utils::getCloseMatches(word, dictionary, 3, 0.6); - + std::cout << " Did you mean: "; if (suggestions.empty()) { std::cout << "No suggestions found."; @@ -457,13 +457,13 @@ } std::cout << std::endl; } - + std::cout << "\nAll examples completed successfully!" << std::endl; - + } catch (const std::exception& e) { std::cerr << "ERROR: " << e.what() << std::endl; return 1; } - + return 0; - } \ No newline at end of file + } diff --git a/example/utils/event_stack.cpp b/example/utils/event_stack.cpp index 6d24f365..d0b6d615 100644 --- a/example/utils/event_stack.cpp +++ b/example/utils/event_stack.cpp @@ -40,4 +40,4 @@ int main() { std::cout << "Compressed errors: " << compressedErrors << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/utils/lcg.cpp b/example/utils/lcg.cpp index 791d82cd..bf7a7d37 100644 --- a/example/utils/lcg.cpp +++ b/example/utils/lcg.cpp @@ -287,4 +287,4 @@ int main() { } spdlog::info("\nAll LCG examples completed successfully!"); return 0; -} \ No newline at end of file +} diff --git a/example/utils/leak.cpp b/example/utils/leak.cpp index 31867b0c..4e609142 100644 --- a/example/utils/leak.cpp +++ b/example/utils/leak.cpp @@ -15,46 +15,46 @@ #include #include #include - + // Include leak detection header first to ensure proper initialization #include "atom/utils/leak.hpp" - + // Additional headers #include "atom/log/loguru.hpp" - + // A class with deliberate memory leak for demonstration class LeakyClass { private: int* data; char* buffer; std::vector* vector_data; - + public: LeakyClass(int size) { // Allocate memory without proper cleanup in some paths data = new int[size]; std::cout << "Allocated int array with " << size << " elements at " << data << std::endl; - + buffer = new char[1024]; std::cout << "Allocated char buffer of 1024 bytes at " << static_cast(buffer) << std::endl; - + vector_data = new std::vector(size, 0.0); std::cout << "Allocated vector with " << size << " elements at " << vector_data << std::endl; } - + // Proper cleanup path void cleanupProperly() { std::cout << "Properly cleaning up all allocations" << std::endl; delete[] data; delete[] buffer; delete vector_data; - + // Set to nullptr to prevent double-free data = nullptr; buffer = nullptr; vector_data = nullptr; } - + // Incomplete cleanup - will cause leak void cleanupIncomplete() { std::cout << "Performing incomplete cleanup (will cause leaks)" << std::endl; @@ -62,104 +62,104 @@ delete[] data; data = nullptr; } - + // No cleanup - will cause all resources to leak void noCleanup() { std::cout << "No cleanup performed (will cause all resources to leak)" << std::endl; // Intentionally do nothing } - + ~LeakyClass() { // In real code, we should clean up here // But for the example, we'll leave it empty to demonstrate leaks std::cout << "~LeakyClass destructor called (without proper cleanup)" << std::endl; } }; - + // Function that demonstrates a memory leak void demonstrateSimpleLeak() { std::cout << "\n=== Demonstrating Simple Memory Leak ===" << std::endl; - + // Allocate memory without freeing it int* leakedArray = new int[100]; for (int i = 0; i < 100; i++) { leakedArray[i] = i; } - + std::cout << "Allocated array at " << leakedArray << " but didn't free it" << std::endl; - + // Note: Deliberately not deleting leakedArray to demonstrate leak detection } - + // Function that demonstrates proper memory management void demonstrateProperMemoryManagement() { std::cout << "\n=== Demonstrating Proper Memory Management ===" << std::endl; - + // Allocate memory and properly free it int* properArray = new int[100]; for (int i = 0; i < 100; i++) { properArray[i] = i; } - + std::cout << "Allocated array at " << properArray << std::endl; - + // Proper cleanup delete[] properArray; std::cout << "Properly freed the array" << std::endl; } - + // Function that demonstrates smart pointers to prevent leaks void demonstrateSmartPointers() { std::cout << "\n=== Demonstrating Smart Pointers ===" << std::endl; - + // Using unique_ptr for automatic cleanup { std::unique_ptr uniqueArray = std::make_unique(100); std::cout << "Created array with unique_ptr at " << uniqueArray.get() << std::endl; - + // Fill with data for (int i = 0; i < 100; i++) { uniqueArray[i] = i; } - + std::cout << "unique_ptr will automatically free memory when going out of scope" << std::endl; } // uniqueArray is automatically deleted here - + // Using shared_ptr for shared ownership { auto sharedVector = std::make_shared>(1000, 0.5); std::cout << "Created vector with shared_ptr at " << sharedVector.get() << std::endl; - + // Create another shared pointer to the same data std::shared_ptr> anotherReference = sharedVector; std::cout << "Created second reference, use count: " << sharedVector.use_count() << std::endl; - + // The data will be freed when all references are gone } // Both shared pointers are automatically deleted here } - + // Function to demonstrate complex leaking scenario across threads void demonstrateThreadedLeaks() { std::cout << "\n=== Demonstrating Threaded Memory Leaks ===" << std::endl; - + // Create a vector to store thread objects std::vector threads; - + // Launch multiple threads that may leak memory for (int i = 0; i < 3; i++) { threads.emplace_back([i]() { std::cout << "Thread " << i << " starting" << std::endl; - + // Allocate memory in thread char* threadBuffer = new char[512 * (i + 1)]; std::memset(threadBuffer, 'A' + i, 512 * (i + 1)); - - std::cout << "Thread " << i << " allocated " << 512 * (i + 1) + + std::cout << "Thread " << i << " allocated " << 512 * (i + 1) << " bytes at " << static_cast(threadBuffer) << std::endl; - + // Sleep to simulate work std::this_thread::sleep_for(std::chrono::milliseconds(100)); - + // Even threads leak differently if (i % 2 == 0) { // Even-numbered threads free their memory @@ -169,33 +169,33 @@ // Odd-numbered threads leak their memory std::cout << "Thread " << i << " is leaking its memory" << std::endl; } - + std::cout << "Thread " << i << " ending" << std::endl; }); } - + // Join all threads for (auto& thread : threads) { thread.join(); } - + std::cout << "All threads completed" << std::endl; } - + // Function to demonstrate leak detection with container classes void demonstrateContainerLeaks() { std::cout << "\n=== Demonstrating Container Leaks ===" << std::endl; - + // Create a vector of raw pointers (not recommended in real code) std::vector pointerVector; - + // Add multiple allocations for (int i = 0; i < 5; i++) { int* ptr = new int(i * 100); pointerVector.push_back(ptr); std::cout << "Added pointer to value " << *ptr << " at " << ptr << std::endl; } - + // Only delete some of them (creating leaks) for (size_t i = 0; i < pointerVector.size(); i++) { if (i % 2 == 0) { @@ -205,87 +205,87 @@ std::cout << "Leaking pointer at index " << i << std::endl; } } - + // Clear the vector (but the odd-indexed pointers are still leaked) pointerVector.clear(); std::cout << "Vector cleared, but some pointers were leaked" << std::endl; } - + // Class to demonstrate RAII pattern to prevent leaks class RAIIExample { private: int* resource; - + public: RAIIExample(int size) : resource(new int[size]) { std::cout << "RAII class allocated resource at " << resource << std::endl; } - + ~RAIIExample() { std::cout << "RAII class automatically freeing resource at " << resource << std::endl; delete[] resource; } }; - + // Function to demonstrate proper RAII usage void demonstrateRAII() { std::cout << "\n=== Demonstrating RAII (Resource Acquisition Is Initialization) ===" << std::endl; - + // Create an instance of the RAII class { RAIIExample raii(200); std::cout << "Using RAII object..." << std::endl; - + // No need to manually call cleanup methods } // Resource is automatically freed here - + std::cout << "RAII object went out of scope, resource was freed" << std::endl; } - + int main() { // Initialize loguru loguru::g_stderr_verbosity = 1; loguru::init(0, nullptr); - + std::cout << "===============================================" << std::endl; std::cout << "Memory Leak Detection Example" << std::endl; std::cout << "===============================================" << std::endl; std::cout << "This example demonstrates how to use the leak detection utility" << std::endl; std::cout << "Note: Visual Leak Detector will report leaks at program exit" << std::endl; std::cout << "===============================================\n" << std::endl; - + // Demonstrate memory leaks with different scenarios demonstrateSimpleLeak(); - + demonstrateProperMemoryManagement(); - + demonstrateSmartPointers(); - + // Create leaky class instances with different cleanup approaches { std::cout << "\n=== Demonstrating Different Cleanup Strategies ===" << std::endl; - + LeakyClass* properCleanup = new LeakyClass(50); LeakyClass* incompleteCleanup = new LeakyClass(100); LeakyClass* noCleanup = new LeakyClass(150); - + // Demonstrate different cleanup strategies properCleanup->cleanupProperly(); incompleteCleanup->cleanupIncomplete(); noCleanup->noCleanup(); - + // Free the class instances delete properCleanup; delete incompleteCleanup; delete noCleanup; } - + demonstrateThreadedLeaks(); - + demonstrateContainerLeaks(); - + demonstrateRAII(); - + std::cout << "\n=== Additional Memory Leak Detection Tips ===" << std::endl; std::cout << "1. Always use smart pointers (std::unique_ptr, std::shared_ptr) when possible" << std::endl; std::cout << "2. Implement RAII pattern in your classes" << std::endl; @@ -293,10 +293,10 @@ std::cout << "4. Use containers and algorithms from the standard library" << std::endl; std::cout << "5. Set clear ownership rules for resources" << std::endl; std::cout << "6. Run with memory leak detection tools regularly" << std::endl; - + std::cout << "\n===============================================" << std::endl; std::cout << "Program completed. Check leak detector output." << std::endl; std::cout << "===============================================" << std::endl; - + return 0; - } \ No newline at end of file + } diff --git a/example/utils/linq.cpp b/example/utils/linq.cpp index b77f0cbe..7e42ade0 100644 --- a/example/utils/linq.cpp +++ b/example/utils/linq.cpp @@ -520,4 +520,4 @@ int main() { << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/utils/print.cpp b/example/utils/print.cpp index 337bbd8f..877e5cbf 100644 --- a/example/utils/print.cpp +++ b/example/utils/print.cpp @@ -425,4 +425,4 @@ int main() { << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/utils/qdatetime.cpp b/example/utils/qdatetime.cpp index b8350089..4daabf37 100644 --- a/example/utils/qdatetime.cpp +++ b/example/utils/qdatetime.cpp @@ -357,4 +357,4 @@ int main() { std::cout << "===============================================" << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/utils/qprocess.cpp b/example/utils/qprocess.cpp index 2ba7ac4f..ba79400f 100644 --- a/example/utils/qprocess.cpp +++ b/example/utils/qprocess.cpp @@ -511,4 +511,4 @@ int main(int argc, char* argv[]) { spdlog::info("======================================================="); return 0; -} \ No newline at end of file +} diff --git a/example/utils/qtimer.cpp b/example/utils/qtimer.cpp index 80788251..46a27188 100644 --- a/example/utils/qtimer.cpp +++ b/example/utils/qtimer.cpp @@ -554,4 +554,4 @@ int main() { << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/utils/qtimezone.cpp b/example/utils/qtimezone.cpp index 8037ddf6..eb582352 100644 --- a/example/utils/qtimezone.cpp +++ b/example/utils/qtimezone.cpp @@ -442,4 +442,4 @@ int main(int argc, char* argv[]) { std::cout << "QTimeZone Example Completed" << std::endl; std::cout << "==================================================" << std::endl; // filepath: examples/qtimezone_example.cpp -} \ No newline at end of file +} diff --git a/example/utils/random.cpp b/example/utils/random.cpp index dfef60df..e8acda9f 100644 --- a/example/utils/random.cpp +++ b/example/utils/random.cpp @@ -201,4 +201,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/utils/ranges.cpp b/example/utils/ranges.cpp index bc1036f5..3742412b 100644 --- a/example/utils/ranges.cpp +++ b/example/utils/ranges.cpp @@ -298,4 +298,4 @@ int main() { std::cout << "\n\n"; return 0; -} \ No newline at end of file +} diff --git a/example/utils/span.cpp b/example/utils/span.cpp index 6cf6cba5..7919fa25 100644 --- a/example/utils/span.cpp +++ b/example/utils/span.cpp @@ -261,4 +261,4 @@ int main() { << atom::utils::standardDeviation(changesSpan) << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/utils/stopwatcher.cpp b/example/utils/stopwatcher.cpp index 4dc3aeb5..df499f08 100644 --- a/example/utils/stopwatcher.cpp +++ b/example/utils/stopwatcher.cpp @@ -342,4 +342,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/utils/string.cpp b/example/utils/string.cpp index beb9c9bc..de299d59 100644 --- a/example/utils/string.cpp +++ b/example/utils/string.cpp @@ -312,4 +312,4 @@ int main() { printCollection(arr, "Array from split"); return 0; -} \ No newline at end of file +} diff --git a/example/utils/switch.cpp b/example/utils/switch.cpp index c2fba1d5..e58b617d 100644 --- a/example/utils/switch.cpp +++ b/example/utils/switch.cpp @@ -464,4 +464,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/utils/time.cpp b/example/utils/time.cpp index 3c34735b..f531320b 100644 --- a/example/utils/time.cpp +++ b/example/utils/time.cpp @@ -48,4 +48,4 @@ int main() { << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/utils/to_any.cpp b/example/utils/to_any.cpp index 64762cfa..3f0404ee 100644 --- a/example/utils/to_any.cpp +++ b/example/utils/to_any.cpp @@ -405,4 +405,4 @@ int main(int argc, char** argv) { } return 0; -} \ No newline at end of file +} diff --git a/example/utils/to_byte.cpp b/example/utils/to_byte.cpp index 6e0353e2..fbad998b 100644 --- a/example/utils/to_byte.cpp +++ b/example/utils/to_byte.cpp @@ -646,4 +646,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/utils/utf.cpp b/example/utils/utf.cpp index 4769ed54..4cee1bd4 100644 --- a/example/utils/utf.cpp +++ b/example/utils/utf.cpp @@ -60,4 +60,4 @@ int main() { std::cout << "Is valid UTF-8: " << std::boolalpha << isValid << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/utils/uuid.cpp b/example/utils/uuid.cpp index 97029e4f..1caa3246 100644 --- a/example/utils/uuid.cpp +++ b/example/utils/uuid.cpp @@ -222,4 +222,4 @@ int main() { #endif return 0; -} \ No newline at end of file +} diff --git a/example/utils/valid_string.cpp b/example/utils/valid_string.cpp index 66690ddd..9ee2fc83 100644 --- a/example/utils/valid_string.cpp +++ b/example/utils/valid_string.cpp @@ -321,4 +321,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/utils/xml.cpp b/example/utils/xml.cpp index ac00b88a..5c9f3a01 100644 --- a/example/utils/xml.cpp +++ b/example/utils/xml.cpp @@ -321,4 +321,4 @@ int main() { std::cout << "Example completed successfully!" << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/web/CMakeLists.txt b/example/web/CMakeLists.txt index 5eb9a2dc..b8e49989 100644 --- a/example/web/CMakeLists.txt +++ b/example/web/CMakeLists.txt @@ -14,20 +14,20 @@ file(GLOB CPP_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) foreach(CPP_FILE ${CPP_FILES}) # 获取不带扩展名的文件名 get_filename_component(EXAMPLE_NAME ${CPP_FILE} NAME_WE) - + # 构造可执行文件名称(子目录名_文件名) set(EXECUTABLE_NAME ${SUBDIR_NAME}_${EXAMPLE_NAME}) - + # 配置选项,允许单独控制每个示例的构建 string(TOUPPER ${EXAMPLE_NAME} EXAMPLE_NAME_UPPER) option(ATOM_EXAMPLE_WEB_${EXAMPLE_NAME_UPPER} "Build web example: ${EXAMPLE_NAME}" ${ATOM_EXAMPLE_WEB_BUILD_ALL}) - + # 有条件地添加可执行文件 if(ATOM_EXAMPLE_WEB_${EXAMPLE_NAME_UPPER}) add_executable(${EXECUTABLE_NAME} ${CPP_FILE}) target_link_libraries(${EXECUTABLE_NAME} atom) - - + + # 设置IDE文件夹分组 set_property(TARGET ${EXECUTABLE_NAME} PROPERTY FOLDER "Examples/Web") endif() diff --git a/example/web/address.cpp b/example/web/address.cpp index 45b0ff48..a6938ca7 100644 --- a/example/web/address.cpp +++ b/example/web/address.cpp @@ -469,4 +469,4 @@ int main() { comprehensiveExample(); return 0; -} \ No newline at end of file +} diff --git a/example/web/curl.cpp b/example/web/curl.cpp index eafcfcdd..6f8754fb 100644 --- a/example/web/curl.cpp +++ b/example/web/curl.cpp @@ -59,4 +59,4 @@ int main() { curl.setMaxDownloadSpeed(1024 * 1024); // 1 MB/s return 0; -} \ No newline at end of file +} diff --git a/example/web/httpparser.cpp b/example/web/httpparser.cpp index 82b59dfb..52c3b0b0 100644 --- a/example/web/httpparser.cpp +++ b/example/web/httpparser.cpp @@ -61,4 +61,4 @@ int main() { std::cout << "Headers cleared." << std::endl; return 0; -} \ No newline at end of file +} diff --git a/example/web/minetype.cpp b/example/web/minetype.cpp index 3d16c9b1..9c3b0459 100644 --- a/example/web/minetype.cpp +++ b/example/web/minetype.cpp @@ -55,4 +55,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/example/web/time.cpp b/example/web/time.cpp index 02a5e97d..4d9adf18 100644 --- a/example/web/time.cpp +++ b/example/web/time.cpp @@ -164,4 +164,4 @@ int main(int argc, char** argv) { } return 0; -} \ No newline at end of file +} diff --git a/example/web/utils.cpp b/example/web/utils.cpp index 9e13a8fb..eab295b8 100644 --- a/example/web/utils.cpp +++ b/example/web/utils.cpp @@ -274,4 +274,4 @@ int main(int argc, char** argv) { LOG_F(INFO, "Network Utils Example Application Completed Successfully"); return 0; -} \ No newline at end of file +} diff --git a/example/xmake.lua b/example/xmake.lua index a44051df..97503b99 100644 --- a/example/xmake.lua +++ b/example/xmake.lua @@ -38,24 +38,24 @@ local example_dirs = { -- Function to build examples from a directory function build_examples_from_dir(dir) local files = os.files(dir .. "/*.cpp") - + for _, file in ipairs(files) do local name = path.basename(file) local example_name = "example_" .. dir:gsub("/", "_") .. "_" .. name - + target(example_name) -- Set target kind to executable set_kind("binary") - + -- Add source file add_files(file) - + -- Add dependencies on atom libraries add_deps("atom") - + -- Add packages add_packages("loguru") - + -- Set output directory set_targetdir("$(buildir)/examples/" .. dir) target_end() diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 1aaf4e53..2da0f1b3 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -101,7 +101,7 @@ foreach(type ${MODULE_TYPES}) # Set include directories target_include_directories( atom_${type} PRIVATE ${CMAKE_SOURCE_DIR}/.. # Atom root directory - ) + ) if(TARGET atom-${type}) target_link_libraries(atom_${type} PRIVATE atom-${type}) endif() # 对web模块特殊处理,确保链接到address组件和utils库 @@ -110,7 +110,7 @@ foreach(type ${MODULE_TYPES}) target_link_libraries(atom_${type} PRIVATE atom-web-address) message(STATUS "Linking atom_web to atom-web-address") endif() - + if(TARGET atom-utils) target_link_libraries(atom_${type} PRIVATE atom-utils) message(STATUS "Linking atom_web to atom-utils") @@ -122,7 +122,7 @@ foreach(type ${MODULE_TYPES}) target_link_libraries(atom_${type} PRIVATE mswsock) message(STATUS "Linking atom_connection to mswsock on Windows") endif() - + # 对algorithm模块特殊处理,确保链接必要的库 if("${type}" STREQUAL "algorithm") if(TARGET atom-type) @@ -130,7 +130,7 @@ foreach(type ${MODULE_TYPES}) message(STATUS "Linking atom_algorithm to atom-type") endif() endif() - + target_link_libraries(atom_${type} PRIVATE loguru atom-error) # Set output name diff --git a/python/algorithm/__init__.py b/python/algorithm/__init__.py index 83c7202c..70578fcd 100644 --- a/python/algorithm/__init__.py +++ b/python/algorithm/__init__.py @@ -1 +1 @@ -# Auto-generated __init__.py for algorithm module \ No newline at end of file +# Auto-generated __init__.py for algorithm module diff --git a/python/algorithm/annealing.cpp b/python/algorithm/annealing.cpp index d8585a58..379a6fc2 100644 --- a/python/algorithm/annealing.cpp +++ b/python/algorithm/annealing.cpp @@ -273,8 +273,8 @@ PYBIND11_MODULE(annealing, m) { R"pbdoc( Calculate a cooling rate for exponential cooling. - This function computes a cooling rate that will reduce the acceptance - probability from an initial value to a final value over the specified + This function computes a cooling rate that will reduce the acceptance + probability from an initial value to a final value over the specified number of iterations. Args: @@ -437,14 +437,14 @@ PYBIND11_MODULE(annealing, m) { py::arg("num_cities") = 20, py::arg("num_runs") = 5, R"pbdoc( Benchmark different cooling strategies for TSP. - + This function runs the simulated annealing algorithm with different cooling strategies and reports the average tour length and execution time. - + Args: num_cities: Number of cities in the random TSP instance (default: 20) num_runs: Number of runs per strategy (default: 5) - + Returns: List of (strategy_name, avg_tour_length, execution_time) tuples )pbdoc"); @@ -489,13 +489,13 @@ PYBIND11_MODULE(annealing, m) { py::arg("cities"), py::arg("tour"), R"pbdoc( Visualize a TSP tour using matplotlib. - + This function plots the cities and the tour path connecting them. - + Args: cities: List of (x,y) coordinates for each city tour: List of city indices representing the tour - + Note: This function requires matplotlib to be installed )pbdoc"); @@ -523,14 +523,14 @@ PYBIND11_MODULE(annealing, m) { py::arg("cities"), py::arg("tour"), R"pbdoc( Compute the total length of a TSP tour. - + This is a convenience function to calculate the total distance of a tour without creating a TSP instance. - + Args: cities: List of (x,y) coordinates for each city tour: List of city indices representing the tour - + Returns: The total distance of the tour )pbdoc"); @@ -580,13 +580,13 @@ PYBIND11_MODULE(annealing, m) { py::arg("cities"), py::arg("start_city") = 0, R"pbdoc( Generate a TSP tour using a greedy nearest neighbor heuristic. - + This function builds a tour by always choosing the closest unvisited city. - + Args: cities: List of (x,y) coordinates for each city start_city: Index of the starting city (default: 0) - + Returns: A tour constructed using the nearest neighbor heuristic )pbdoc"); @@ -640,15 +640,15 @@ PYBIND11_MODULE(annealing, m) { py::arg("cities"), py::arg("tour"), py::arg("max_iterations") = 1000, R"pbdoc( Improve a TSP tour using the 2-opt local search heuristic. - + This algorithm iteratively removes two edges and reconnects the tour in the other possible way, keeping the change if it improves the tour length. - + Args: cities: List of (x,y) coordinates for each city tour: Initial tour to improve max_iterations: Maximum number of improvement iterations - + Returns: An improved tour )pbdoc"); @@ -667,11 +667,11 @@ class CustomProblem: Example custom problem implementation compatible with the C++ AnnealingProblem concept. Replace with your own problem definition. """ - + def __init__(self, problem_data: Any): """Initialize your problem with specific data""" self.problem_data = problem_data - + def energy(self, solution: Any) -> float: """ Calculate the objective function value (energy) of a solution. @@ -679,17 +679,17 @@ class CustomProblem: """ # Replace with your actual objective function return 0.0 - + def neighbor(self, solution: Any) -> Any: """Generate a slightly modified neighboring solution""" # Replace with your neighbor generation logic return solution - + def random_solution(self) -> Any: """Generate a random initial solution""" # Replace with code to generate a valid random solution return None - + def validate(self, solution: Any) -> bool: """Check if a solution is valid""" # Replace with your validation logic @@ -698,29 +698,29 @@ class CustomProblem: # Example usage with the atom.algorithm.annealing module: def solve_custom_problem(): from atom.algorithm.annealing import SimulatedAnnealing, AnnealingStrategy - + # Create your problem instance problem = CustomProblem(your_problem_data) - + # Set up the annealing solver annealing = SimulatedAnnealing(problem) annealing.set_max_iterations(10000) annealing.set_initial_temperature(100.0) annealing.set_cooling_strategy(AnnealingStrategy.EXPONENTIAL) - + # Run the optimization best_solution = annealing.optimize() - + return best_solution )code"; }, R"pbdoc( Provides a Python template for creating custom problem types. - + This function returns a string containing Python code that shows how to create a custom problem compatible with the simulated annealing algorithm interface. - + Returns: Python code template as a string )pbdoc"); diff --git a/python/algorithm/base.cpp b/python/algorithm/base.cpp index b33a0542..213f7c1c 100644 --- a/python/algorithm/base.cpp +++ b/python/algorithm/base.cpp @@ -10,12 +10,12 @@ PYBIND11_MODULE(base, m) { m.doc() = R"pbdoc( Base Encoding/Decoding Algorithms --------------------------------- - + This module provides functions for encoding and decoding data in various formats: - Base32 encoding and decoding - Base64 encoding and decoding - XOR encryption and decryption - + Examples: >>> import atom.algorithm.base as base >>> base.base64_encode("Hello, world!") @@ -56,16 +56,16 @@ PYBIND11_MODULE(base, m) { }, py::arg("data"), R"pbdoc( Encode binary data using Base32. - + Args: data (bytes): The binary data to encode. - + Returns: str: The Base32 encoded string. - + Raises: ValueError: If encoding fails. - + Example: >>> encode_base32(b'hello') 'NBSWY3DP' @@ -85,16 +85,16 @@ PYBIND11_MODULE(base, m) { }, py::arg("encoded"), R"pbdoc( Decode a Base32 encoded string back to binary data. - + Args: encoded (str): The Base32 encoded string. - + Returns: bytes: The decoded binary data. - + Raises: ValueError: If decoding fails. - + Example: >>> decode_base32('NBSWY3DP') b'hello' @@ -113,17 +113,17 @@ PYBIND11_MODULE(base, m) { }, py::arg("input"), py::arg("padding") = true, R"pbdoc( Encode a string using Base64. - + Args: input (str): The string to encode. padding (bool, optional): Whether to add padding characters. Defaults to True. - + Returns: str: The Base64 encoded string. - + Raises: ValueError: If encoding fails. - + Example: >>> base64_encode("hello") 'aGVsbG8=' @@ -143,16 +143,16 @@ PYBIND11_MODULE(base, m) { }, py::arg("input"), R"pbdoc( Decode a Base64 encoded string. - + Args: input (str): The Base64 encoded string. - + Returns: str: The decoded string. - + Raises: ValueError: If decoding fails. - + Example: >>> base64_decode('aGVsbG8=') 'hello' @@ -163,14 +163,14 @@ PYBIND11_MODULE(base, m) { py::arg("key"), R"pbdoc( Encrypt a string using XOR algorithm. - + Args: plaintext (str): The string to encrypt. key (int): The encryption key (0-255). - + Returns: str: The encrypted string. - + Example: >>> encrypted = xor_encrypt("hello", 42) >>> # Result is binary data @@ -180,14 +180,14 @@ PYBIND11_MODULE(base, m) { py::arg("key"), R"pbdoc( Decrypt a string using XOR algorithm. - + Args: ciphertext (str): The encrypted string. key (int): The decryption key (0-255). - + Returns: str: The decrypted string. - + Example: >>> encrypted = xor_encrypt("hello", 42) >>> xor_decrypt(encrypted, 42) @@ -198,13 +198,13 @@ PYBIND11_MODULE(base, m) { m.def("is_base64", &atom::algorithm::isBase64, py::arg("str"), R"pbdoc( Check if a string is a valid Base64 encoded string. - + Args: str (str): The string to validate. - + Returns: bool: True if the string is valid Base64, False otherwise. - + Example: >>> is_base64('aGVsbG8=') True @@ -227,14 +227,14 @@ PYBIND11_MODULE(base, m) { }, py::arg("input"), py::arg("padding") = true, R"pbdoc( Encode binary data using Base64. - + Args: input (bytes): The binary data to encode. padding (bool, optional): Whether to add padding characters. Defaults to True. - + Returns: str: The Base64 encoded string. - + Example: >>> base64_encode_binary(b'\x00\x01\x02\x03') 'AAECAw==' @@ -252,13 +252,13 @@ PYBIND11_MODULE(base, m) { }, py::arg("input"), R"pbdoc( Decode a Base64 encoded string to binary data. - + Args: input (str): The Base64 encoded string. - + Returns: bytes: The decoded binary data. - + Example: >>> base64_decode_binary('AAECAw==') b'\x00\x01\x02\x03' @@ -279,13 +279,13 @@ PYBIND11_MODULE(base, m) { }, py::arg("input"), py::arg("padding") = true, R"pbdoc( Encode binary data using Base64 (returns bytes). - + This function matches the API of Python's `base64.b64encode`. - + Args: input (bytes): The binary data to encode. padding (bool, optional): Whether to add padding characters. Defaults to True. - + Returns: bytes: The Base64 encoded data as bytes. )pbdoc"); @@ -304,12 +304,12 @@ PYBIND11_MODULE(base, m) { }, py::arg("input"), R"pbdoc( Decode Base64 encoded data (accepts bytes). - + This function matches the API of Python's `base64.b64decode`. - + Args: input (bytes): The Base64 encoded data. - + Returns: bytes: The decoded binary data. )pbdoc"); @@ -338,11 +338,11 @@ PYBIND11_MODULE(base, m) { }, py::arg("plaintext"), py::arg("key"), R"pbdoc( Encrypt binary data using XOR algorithm. - + Args: plaintext (bytes): The binary data to encrypt. key (int): The encryption key (0-255). - + Returns: bytes: The encrypted data. )pbdoc"); @@ -357,11 +357,11 @@ PYBIND11_MODULE(base, m) { }, py::arg("ciphertext"), py::arg("key"), R"pbdoc( Decrypt binary data using XOR algorithm. - + Args: ciphertext (bytes): The encrypted data. key (int): The decryption key (0-255). - + Returns: bytes: The decrypted data. )pbdoc"); @@ -411,20 +411,20 @@ PYBIND11_MODULE(base, m) { }, py::arg("data"), py::arg("thread_count") = 0, py::arg("func"), R"pbdoc( Process binary data in parallel across multiple threads. - + Args: data (bytes): The binary data to process. thread_count (int, optional): Number of threads to use. Default is 0 (auto). func (callable): Function that processes a chunk of data. Should accept and return bytes objects of the same size. - + Returns: bytes: The processed data. - + Example: >>> def process_chunk(chunk): ... return bytes(b ^ 42 for b in chunk) >>> parallel_process(b'hello world', 2, process_chunk) b'*\x0f\x16\x16\x17K\x04\x17\x03\x16\x0e' )pbdoc"); -} \ No newline at end of file +} diff --git a/python/algorithm/blowfish.cpp b/python/algorithm/blowfish.cpp index 11b99208..7fae3ced 100644 --- a/python/algorithm/blowfish.cpp +++ b/python/algorithm/blowfish.cpp @@ -8,10 +8,10 @@ PYBIND11_MODULE(blowfish, m) { m.doc() = R"pbdoc( Blowfish Encryption Algorithm ---------------------------- - + This module provides a Python interface to the Blowfish encryption algorithm. Blowfish is a symmetric-key block cipher designed by Bruce Schneier in 1993. - + Example: >>> import atom.algorithm.blowfish as bf >>> # Generate a random key @@ -47,10 +47,10 @@ PYBIND11_MODULE(blowfish, m) { // Blowfish class py::class_(m, "Blowfish", R"pbdoc( Blowfish cipher implementation. - + The Blowfish class implements the Blowfish encryption algorithm, a symmetric key block cipher that can be used for encrypting data. - + Args: key (bytes): The encryption key (4-56 bytes) )pbdoc") @@ -94,13 +94,13 @@ PYBIND11_MODULE(blowfish, m) { }, py::arg("block"), R"pbdoc( Encrypt a single 8-byte block. - + Args: block (bytes): The 8-byte block to encrypt - + Returns: bytes: The encrypted 8-byte block - + Raises: ValueError: If the block is not exactly 8 bytes )pbdoc") @@ -127,13 +127,13 @@ PYBIND11_MODULE(blowfish, m) { }, py::arg("block"), R"pbdoc( Decrypt a single 8-byte block. - + Args: block (bytes): The 8-byte block to decrypt - + Returns: bytes: The decrypted 8-byte block - + Raises: ValueError: If the block is not exactly 8 bytes )pbdoc") @@ -156,16 +156,16 @@ PYBIND11_MODULE(blowfish, m) { }, py::arg("data"), R"pbdoc( Encrypt arbitrary data. - + This method encrypts arbitrary data using the Blowfish cipher. PKCS7 padding is automatically applied. - + Args: data (bytes): The data to encrypt - + Returns: bytes: The encrypted data - + Raises: ValueError: If the data is empty )pbdoc") @@ -200,16 +200,16 @@ PYBIND11_MODULE(blowfish, m) { }, py::arg("data"), R"pbdoc( Decrypt data. - + This method decrypts data that was encrypted with the encrypt_data method. PKCS7 padding is automatically removed. - + Args: data (bytes): The encrypted data - + Returns: bytes: The decrypted data - + Raises: ValueError: If the data is empty or not a multiple of 8 bytes )pbdoc") @@ -217,14 +217,14 @@ PYBIND11_MODULE(blowfish, m) { py::arg("input_file"), py::arg("output_file"), R"pbdoc( Encrypt a file. - + This method reads a file, encrypts its contents, and writes the encrypted data to another file. - + Args: input_file (str): Path to the input file output_file (str): Path to the output file - + Raises: RuntimeError: If file operations fail )pbdoc") @@ -232,14 +232,14 @@ PYBIND11_MODULE(blowfish, m) { py::arg("input_file"), py::arg("output_file"), R"pbdoc( Decrypt a file. - + This method reads an encrypted file, decrypts its contents, and writes the decrypted data to another file. - + Args: input_file (str): Path to the encrypted file output_file (str): Path to the output file - + Raises: RuntimeError: If file operations fail )pbdoc"); @@ -260,14 +260,14 @@ PYBIND11_MODULE(blowfish, m) { }, py::arg("length") = 16, R"pbdoc( Generate a cryptographically secure random key. - + Args: length (int, optional): The key length in bytes. Default is 16. Must be between 4 and 56 bytes. - + Returns: bytes: A random key of the specified length - + Raises: ValueError: If the length is not between 4 and 56 bytes )pbdoc"); @@ -288,11 +288,11 @@ PYBIND11_MODULE(blowfish, m) { }, py::arg("cipher"), py::arg("text"), R"pbdoc( Encrypt a string using a Blowfish cipher. - + Args: cipher (Blowfish): The Blowfish cipher instance text (str): The string to encrypt - + Returns: bytes: The encrypted data )pbdoc"); @@ -326,14 +326,14 @@ PYBIND11_MODULE(blowfish, m) { }, py::arg("cipher"), py::arg("data"), R"pbdoc( Decrypt data to a string using a Blowfish cipher. - + Args: cipher (Blowfish): The Blowfish cipher instance data (bytes): The encrypted data - + Returns: str: The decrypted string - + Raises: ValueError: If the data is empty or not a multiple of 8 bytes UnicodeDecodeError: If the decrypted data is not valid UTF-8 @@ -379,17 +379,17 @@ PYBIND11_MODULE(blowfish, m) { }, py::arg("password"), py::arg("data"), R"pbdoc( Encrypt data using a password. - + WARNING: This is a convenience function with a simple key derivation. For secure applications, use a proper key derivation function. - + Args: password (str): The password data (bytes): The data to encrypt - + Returns: bytes: The encrypted data - + Raises: ValueError: If the password is empty or data is empty )pbdoc"); @@ -439,18 +439,18 @@ PYBIND11_MODULE(blowfish, m) { }, py::arg("password"), py::arg("data"), R"pbdoc( Decrypt data using a password. - + WARNING: This is a convenience function with a simple key derivation. For secure applications, use a proper key derivation function. - + Args: password (str): The password data (bytes): The encrypted data - + Returns: bytes: The decrypted data - + Raises: ValueError: If the password is empty, data is empty, or data is not a multiple of 8 bytes )pbdoc"); -} \ No newline at end of file +} diff --git a/python/algorithm/error_calibration.cpp b/python/algorithm/error_calibration.cpp index bfd9a86f..7b77ba1a 100644 --- a/python/algorithm/error_calibration.cpp +++ b/python/algorithm/error_calibration.cpp @@ -29,24 +29,24 @@ PYBIND11_MODULE(error_calibration, m) { This module provides tools for error calibration of measurement data. It includes methods for linear, polynomial, exponential, logarithmic, and power law calibration, as well as tools for statistical analysis. - + Examples: >>> import numpy as np >>> from atom.algorithm.error_calibration import ErrorCalibration - >>> + >>> >>> # Sample data >>> measured = [1.0, 2.0, 3.0, 4.0, 5.0] >>> actual = [0.9, 2.1, 2.8, 4.2, 4.9] - >>> + >>> >>> # Create calibrator and perform linear calibration >>> calibrator = ErrorCalibration() >>> calibrator.linear_calibrate(measured, actual) - >>> + >>> >>> # Print calibration parameters >>> print(f"Slope: {calibrator.get_slope()}") >>> print(f"Intercept: {calibrator.get_intercept()}") >>> print(f"R-squared: {calibrator.get_r_squared()}") - >>> + >>> >>> # Apply calibration to new measurements >>> new_measurement = 3.5 >>> calibrated_value = calibrator.apply(new_measurement) @@ -79,7 +79,7 @@ PYBIND11_MODULE(error_calibration, m) { py::class_>(m, "ErrorCalibration", R"pbdoc( Error calibration class for measurement data. - + This class provides methods for calibrating measurements and analyzing errors using various calibration techniques, including linear, polynomial, exponential, logarithmic, and power law models. @@ -90,11 +90,11 @@ PYBIND11_MODULE(error_calibration, m) { py::arg("measured"), py::arg("actual"), R"pbdoc( Perform linear calibration using the least squares method. - + Args: measured: List of measured values actual: List of actual values - + Raises: ValueError: If input vectors are empty or of unequal size )pbdoc") @@ -103,12 +103,12 @@ PYBIND11_MODULE(error_calibration, m) { py::arg("measured"), py::arg("actual"), py::arg("degree"), R"pbdoc( Perform polynomial calibration using the least squares method. - + Args: measured: List of measured values actual: List of actual values degree: Degree of the polynomial - + Raises: ValueError: If input vectors are empty, of unequal size, or if degree is invalid )pbdoc") @@ -117,11 +117,11 @@ PYBIND11_MODULE(error_calibration, m) { py::arg("measured"), py::arg("actual"), R"pbdoc( Perform exponential calibration using the least squares method. - + Args: measured: List of measured values actual: List of actual values - + Raises: ValueError: If input vectors are empty, of unequal size, or if actual values are not positive )pbdoc") @@ -130,11 +130,11 @@ PYBIND11_MODULE(error_calibration, m) { py::arg("measured"), py::arg("actual"), R"pbdoc( Perform logarithmic calibration using the least squares method. - + Args: measured: List of measured values actual: List of actual values - + Raises: ValueError: If input vectors are empty, of unequal size, or if measured values are not positive )pbdoc") @@ -143,11 +143,11 @@ PYBIND11_MODULE(error_calibration, m) { py::arg("measured"), py::arg("actual"), R"pbdoc( Perform power law calibration using the least squares method. - + Args: measured: List of measured values actual: List of actual values - + Raises: ValueError: If input vectors are empty, of unequal size, or if values are not positive )pbdoc") @@ -164,10 +164,10 @@ PYBIND11_MODULE(error_calibration, m) { py::arg("filename"), R"pbdoc( Save residuals to a CSV file for plotting. - + Args: filename: Path to the output file - + Raises: IOError: If the file cannot be opened )pbdoc") @@ -178,16 +178,16 @@ PYBIND11_MODULE(error_calibration, m) { py::arg("n_iterations") = 1000, py::arg("confidence_level") = 0.95, R"pbdoc( Calculate bootstrap confidence interval for the slope. - + Args: measured: List of measured values actual: List of actual values n_iterations: Number of bootstrap iterations (default: 1000) confidence_level: Confidence level (default: 0.95) - + Returns: Tuple of lower and upper bounds of the confidence interval - + Raises: ValueError: If input parameters are invalid )pbdoc") @@ -196,15 +196,15 @@ PYBIND11_MODULE(error_calibration, m) { py::arg("measured"), py::arg("actual"), py::arg("threshold") = 2.0, R"pbdoc( Detect outliers using the residuals of the calibration. - + Args: measured: List of measured values actual: List of actual values threshold: Z-score threshold for outlier detection (default: 2.0) - + Returns: Tuple of mean residual, standard deviation, and threshold - + Raises: RuntimeError: If metrics have not been calculated yet )pbdoc") @@ -213,12 +213,12 @@ PYBIND11_MODULE(error_calibration, m) { py::arg("measured"), py::arg("actual"), py::arg("k") = 5, R"pbdoc( Perform k-fold cross-validation of the calibration. - + Args: measured: List of measured values actual: List of actual values k: Number of folds (default: 5) - + Raises: ValueError: If input vectors are invalid RuntimeError: If all cross-validation folds fail @@ -249,7 +249,7 @@ PYBIND11_MODULE(error_calibration, m) { py::class_>( m, "ErrorCalibrationFloat", R"pbdoc( Error calibration class with single precision (float). - + This class is identical to ErrorCalibration but uses single precision floating point calculations, which may be faster but less accurate. )pbdoc") @@ -317,17 +317,17 @@ PYBIND11_MODULE(error_calibration, m) { py::arg("measured"), py::arg("actual"), R"pbdoc( Perform asynchronous linear calibration. - + This function starts a calibration in a background thread and returns the calibrator once the calibration is complete. - + Args: measured: List of measured values actual: List of actual values - + Returns: ErrorCalibration object with the calibration results - + Raises: ValueError: If the calibration fails )pbdoc"); @@ -406,17 +406,17 @@ PYBIND11_MODULE(error_calibration, m) { py::arg("measured"), py::arg("actual"), R"pbdoc( Find the best calibration method for the given data. - + This function tries different calibration methods and returns the name of the method with the lowest Mean Squared Error (MSE). - + Args: measured: List of measured values actual: List of actual values - + Returns: String with the name of the best calibration method - + Raises: ValueError: If all calibration methods fail )pbdoc"); @@ -439,11 +439,11 @@ PYBIND11_MODULE(error_calibration, m) { py::arg("measured_array"), py::arg("calibrator"), R"pbdoc( Apply calibration to a numpy array of measurements. - + Args: measured_array: Numpy array of measured values calibrator: ErrorCalibration object - + Returns: Numpy array of calibrated values )pbdoc"); @@ -502,18 +502,18 @@ PYBIND11_MODULE(error_calibration, m) { py::arg("measured"), py::arg("actual"), py::arg("calibrator"), R"pbdoc( Plot calibration results using matplotlib. - + This function creates a scatter plot of measured vs actual values, as well as the calibrated values and the calibration line. - + Args: measured: List of measured values actual: List of actual values calibrator: ErrorCalibration object - + Returns: True if the plot was created successfully, False otherwise - + Note: This function requires matplotlib to be installed. )pbdoc"); @@ -656,21 +656,21 @@ PYBIND11_MODULE(error_calibration, m) { py::arg("calibrator"), py::arg("measured"), py::arg("actual"), R"pbdoc( Analyze residuals with comprehensive plots and statistics. - + This function creates a set of diagnostic plots for analyzing residuals: 1. Residuals vs measured values 2. Histogram of residuals 3. Q-Q plot for normality check 4. Calibration curve - + Args: calibrator: ErrorCalibration object measured: List of measured values actual: List of actual values - + Returns: Dictionary with residual statistics (mean, std_dev, mse, mae, r_squared, slope, intercept) - + Note: This function requires matplotlib and scipy to be installed. )pbdoc"); diff --git a/python/algorithm/flood.cpp b/python/algorithm/flood.cpp index 481e07b1..3d0bd837 100644 --- a/python/algorithm/flood.cpp +++ b/python/algorithm/flood.cpp @@ -53,7 +53,7 @@ PYBIND11_MODULE(flood_fill, m) { -------------------- This module provides various flood fill algorithms for 2D grids: - + - **fill_bfs**: Flood fill using Breadth-First Search - **fill_dfs**: Flood fill using Depth-First Search - **fill_parallel**: Flood fill using multiple threads @@ -61,14 +61,14 @@ PYBIND11_MODULE(flood_fill, m) { Example: >>> import numpy as np >>> from atom.algorithm.flood_fill import fill_bfs, Connectivity - >>> + >>> >>> # Create a grid >>> grid = np.zeros((10, 10), dtype=np.int32) >>> grid[3:7, 3:7] = 1 # Create a square - >>> + >>> >>> # Fill the square >>> filled_grid = fill_bfs(grid, 5, 5, 1, 2, Connectivity.FOUR) - >>> + >>> >>> # Check result >>> assert np.all(filled_grid[3:7, 3:7] == 2) )pbdoc"; @@ -518,4 +518,4 @@ PYBIND11_MODULE(flood_fill, m) { Raises: RuntimeError: If image is not 3D or doesn't have 3 channels )pbdoc"); -} \ No newline at end of file +} diff --git a/python/algorithm/fnmatch.cpp b/python/algorithm/fnmatch.cpp index bb21d26d..9e3178f9 100644 --- a/python/algorithm/fnmatch.cpp +++ b/python/algorithm/fnmatch.cpp @@ -13,24 +13,24 @@ PYBIND11_MODULE(fnmatch, m) { This module provides pattern matching functionality similar to Python's fnmatch, but with additional features and optimizations: - + - Case-insensitive matching - Path-aware matching - SIMD-accelerated matching (when available) - Support for multiple patterns - Parallel processing options - + Example: >>> from atom.algorithm import fnmatch - >>> + >>> >>> # Simple pattern matching >>> fnmatch.fnmatch("example.txt", "*.txt") True - + >>> # Case-insensitive matching >>> fnmatch.fnmatch("Example.TXT", "*.txt", fnmatch.CASEFOLD) True - + >>> # Filter a list of filenames >>> names = ["file1.txt", "file2.jpg", "file3.txt", "file4.png"] >>> fnmatch.filter(names, "*.txt") @@ -61,16 +61,16 @@ PYBIND11_MODULE(fnmatch, m) { py::arg("pattern"), py::arg("string"), py::arg("flags") = 0, R"pbdoc( Matches a string against a specified pattern. - + Args: pattern: The pattern to match against string: The string to match flags: Optional flags to modify matching behavior (default: 0) Can be NOESCAPE, PATHNAME, PERIOD, CASEFOLD or combined with bitwise OR - + Returns: bool: True if the string matches the pattern, False otherwise - + Raises: FnmatchException: If there is an error in the pattern )pbdoc"); @@ -81,12 +81,12 @@ PYBIND11_MODULE(fnmatch, m) { py::arg("pattern"), py::arg("string"), py::arg("flags") = 0, R"pbdoc( Matches a string against a specified pattern without throwing exceptions. - + Args: pattern: The pattern to match against string: The string to match flags: Optional flags to modify matching behavior (default: 0) - + Returns: Expected object containing bool result or FnmatchError )pbdoc"); @@ -106,12 +106,12 @@ PYBIND11_MODULE(fnmatch, m) { py::arg("names"), py::arg("pattern"), py::arg("flags") = 0, R"pbdoc( Check if any string in the list matches the pattern. - + Args: names: List of strings to filter pattern: Pattern to filter with flags: Optional flags to modify filtering behavior (default: 0) - + Returns: bool: True if any element matches the pattern )pbdoc"); @@ -147,13 +147,13 @@ PYBIND11_MODULE(fnmatch, m) { py::arg("use_parallel") = true, R"pbdoc( Filter a list of strings with multiple patterns. - + Args: names: List of strings to filter patterns: List of patterns to filter with flags: Optional flags to modify filtering behavior (default: 0) use_parallel: Whether to use parallel execution (default: True) - + Returns: list: Strings from names that match any pattern in patterns )pbdoc"); @@ -163,15 +163,15 @@ PYBIND11_MODULE(fnmatch, m) { py::arg("pattern"), py::arg("flags") = 0, R"pbdoc( Translate a pattern into a regular expression string. - + Args: pattern: The pattern to translate flags: Optional flags to modify translation behavior (default: 0) - + Returns: Expected object containing regex string or FnmatchError )pbdoc"); // Add version information m.attr("__version__") = "1.0.0"; -} \ No newline at end of file +} diff --git a/python/algorithm/fraction.cpp b/python/algorithm/fraction.cpp index df8b1a13..d5f79954 100644 --- a/python/algorithm/fraction.cpp +++ b/python/algorithm/fraction.cpp @@ -13,22 +13,22 @@ PYBIND11_MODULE(fraction, m) { ----------------------------- This module provides a robust fraction class for exact rational arithmetic. - + The Fraction class represents rational numbers as a numerator and denominator, always keeping the fraction in reduced form. It supports all standard arithmetic operations, comparison, conversion to various types, and additional utilities. Example: >>> from atom.algorithm import fraction - >>> + >>> >>> # Create fractions >>> a = fraction.Fraction(1, 2) # 1/2 >>> b = fraction.Fraction(3, 4) # 3/4 - >>> + >>> >>> # Arithmetic operations >>> c = a + b # 5/4 >>> print(c) # "5/4" - >>> + >>> >>> # Converting from floats >>> d = fraction.make_fraction(0.333333) # Approximate as a fraction >>> print(d) # "1/3" or a close approximation @@ -67,18 +67,18 @@ various arithmetic operations, comparisons, and conversions. Examples: >>> from atom.algorithm.fraction import Fraction - >>> + >>> >>> # Create a fraction >>> f1 = Fraction(1, 2) # 1/2 >>> f2 = Fraction(3, 4) # 3/4 - >>> + >>> >>> # Basic arithmetic >>> f3 = f1 + f2 # 5/4 >>> f4 = f1 * f2 # 3/8 - >>> + >>> >>> # Comparisons >>> f1 < f2 # True - >>> + >>> >>> # Conversion >>> float(f1) # 0.5 )") @@ -283,4 +283,4 @@ various arithmetic operations, comparisons, and conversions. >>> lcm(4, 6) # Returns 12 >>> lcm(15, 25) # Returns 75 )"); -} \ No newline at end of file +} diff --git a/python/algorithm/hash.cpp b/python/algorithm/hash.cpp index 12655051..9dc84d81 100644 --- a/python/algorithm/hash.cpp +++ b/python/algorithm/hash.cpp @@ -13,29 +13,29 @@ PYBIND11_MODULE(hash, m) { This module provides a collection of optimized hash functions with thread-safe caching, parallel processing capability, and support for various data types. - + The module includes: - Standard hash functions optimized with SIMD instructions - Support for various hash algorithms (STD, FNV1A, etc.) - Utilities for combining and verifying hash values - Thread-safe hash caching - Hash computation for complex data structures - + Example: >>> from atom.algorithm import hash - >>> + >>> >>> # Compute hash of a string >>> h1 = hash.compute_hash("Hello, world!") >>> print(h1) - + >>> # Compute hash with a specific algorithm >>> h2 = hash.compute_hash("Hello, world!", hash.HashAlgorithm.FNV1A) >>> print(h2) - + >>> # Hash a list of values >>> h3 = hash.compute_hash([1, 2, 3, 4, 5]) >>> print(h3) - + >>> # Verify if two hashes match >>> hash.verify_hash(h1, h2) # False >>> hash.verify_hash(h1, h1) # True @@ -66,11 +66,11 @@ PYBIND11_MODULE(hash, m) { py::arg("algorithm") = atom::algorithm::HashAlgorithm::STD, R"pbdoc( Compute the hash value of a string. - + Args: value: The string value to hash algorithm: The hash algorithm to use (default: STD) - + Returns: The computed hash value )pbdoc"); @@ -85,11 +85,11 @@ PYBIND11_MODULE(hash, m) { py::arg("algorithm") = atom::algorithm::HashAlgorithm::STD, R"pbdoc( Compute the hash value of an integer. - + Args: value: The integer value to hash algorithm: The hash algorithm to use (default: STD) - + Returns: The computed hash value )pbdoc"); @@ -104,11 +104,11 @@ PYBIND11_MODULE(hash, m) { py::arg("algorithm") = atom::algorithm::HashAlgorithm::STD, R"pbdoc( Compute the hash value of a float. - + Args: value: The float value to hash algorithm: The hash algorithm to use (default: STD) - + Returns: The computed hash value )pbdoc"); @@ -123,11 +123,11 @@ PYBIND11_MODULE(hash, m) { py::arg("algorithm") = atom::algorithm::HashAlgorithm::STD, R"pbdoc( Compute the hash value of a boolean. - + Args: value: The boolean value to hash algorithm: The hash algorithm to use (default: STD) - + Returns: The computed hash value )pbdoc"); @@ -143,11 +143,11 @@ PYBIND11_MODULE(hash, m) { py::arg("algorithm") = atom::algorithm::HashAlgorithm::STD, R"pbdoc( Compute the hash value of a bytes object. - + Args: value: The bytes object to hash algorithm: The hash algorithm to use (default: STD) - + Returns: The computed hash value )pbdoc"); @@ -168,10 +168,10 @@ PYBIND11_MODULE(hash, m) { py::arg("value"), R"pbdoc( Compute the hash value of a tuple. - + Args: value: The tuple to hash - + Returns: The computed hash value )pbdoc"); @@ -210,11 +210,11 @@ PYBIND11_MODULE(hash, m) { py::arg("value"), py::arg("parallel") = false, R"pbdoc( Compute the hash value of a list. - + Args: value: The list to hash parallel: Whether to use parallel processing for large lists (default: False) - + Returns: The computed hash value )pbdoc"); @@ -245,10 +245,10 @@ PYBIND11_MODULE(hash, m) { py::arg("value"), R"pbdoc( Compute the hash value of a dictionary. - + Args: value: The dictionary to hash - + Returns: The computed hash value )pbdoc"); @@ -275,10 +275,10 @@ PYBIND11_MODULE(hash, m) { py::arg("value"), R"pbdoc( Compute the hash value of a set. - + Args: value: The set to hash - + Returns: The computed hash value )pbdoc"); @@ -289,10 +289,10 @@ PYBIND11_MODULE(hash, m) { py::arg("value"), R"pbdoc( Compute the hash value of None. - + Args: value: None - + Returns: The hash value of None (0) )pbdoc"); @@ -306,11 +306,11 @@ PYBIND11_MODULE(hash, m) { py::arg("value"), py::arg("basis") = 2166136261u, R"pbdoc( Compute the FNV-1a hash of a string. - + Args: value: The string to hash basis: The initial basis value (default: 2166136261) - + Returns: The computed FNV-1a hash value )pbdoc"); @@ -320,13 +320,13 @@ PYBIND11_MODULE(hash, m) { py::arg("hash"), R"pbdoc( Combine two hash values into one. - + This function is useful for creating hash values for composite objects. - + Args: seed: The initial hash value hash: The hash value to combine with the seed - + Returns: The combined hash value )pbdoc"); @@ -336,12 +336,12 @@ PYBIND11_MODULE(hash, m) { py::arg("hash2"), py::arg("tolerance") = 0, R"pbdoc( Verify if two hash values match. - + Args: hash1: The first hash value hash2: The second hash value tolerance: Allowed difference for fuzzy matching (default: 0) - + Returns: True if the hashes match within the tolerance, False otherwise )pbdoc"); @@ -355,12 +355,12 @@ PYBIND11_MODULE(hash, m) { py::arg("str"), R"pbdoc( Compute the hash value of a string using the FNV-1a algorithm. - + This is equivalent to the _hash string literal operator in C++. - + Args: str: The string to hash - + Returns: The computed hash value )pbdoc"); @@ -396,12 +396,12 @@ PYBIND11_MODULE(hash, m) { py::arg("filename"), R"pbdoc( Generate a fast hash for a filename. - + This is useful for creating unique identifiers for files. - + Args: filename: The filename to hash - + Returns: The computed hash value )pbdoc"); @@ -438,11 +438,11 @@ PYBIND11_MODULE(hash, m) { py::arg("value"), py::arg("iterations") = 100000, R"pbdoc( Benchmark different hash algorithms. - + Args: value: The string to hash iterations: Number of iterations to run (default: 100000) - + Returns: A dictionary with algorithm names as keys and tuples (time, hash_value) as values )pbdoc"); @@ -498,15 +498,15 @@ PYBIND11_MODULE(hash, m) { py::arg("algorithm") = atom::algorithm::HashAlgorithm::STD, R"pbdoc( Analyze the distribution of hash values for a list of inputs. - + Args: values: The list of values to hash algorithm: The hash algorithm to use (default: STD) - + Returns: A dictionary with distribution metrics )pbdoc"); // Add version information m.attr("__version__") = "1.0.0"; -} \ No newline at end of file +} diff --git a/python/algorithm/huffman.cpp b/python/algorithm/huffman.cpp index d312bc40..9461b581 100644 --- a/python/algorithm/huffman.cpp +++ b/python/algorithm/huffman.cpp @@ -9,39 +9,39 @@ PYBIND11_MODULE(huffman, m) { m.doc() = R"pbdoc( Huffman Encoding and Compression ------------------------------- - + This module provides functions for compressing and decompressing data using Huffman encoding, an efficient variable-length prefix coding algorithm. - + **Basic Usage:** - + ```python from atom.algorithm.huffman import compress, decompress - + # Compress some data data = b"This is an example string with repeating characters" compressed_data, serialized_tree = compress(data) - + # Print compression statistics print(f"Original size: {len(data)} bytes") print(f"Compressed size: {len(compressed_data) // 8} bytes") print(f"Compression ratio: {len(compressed_data) / (len(data) * 8):.2%}") - + # Decompress the data decompressed_data = decompress(compressed_data, serialized_tree) - + # Verify the data matches assert data == decompressed_data ``` - + **Convenience Functions:** - + For simpler usage with built-in serialization: - + ```python from atom.algorithm.huffman import encode, decode - + compressed = encode(b"Hello, world!") original = decode(compressed) ``` @@ -95,13 +95,13 @@ PYBIND11_MODULE(huffman, m) { py::arg("frequencies"), R"pbdoc( Create a Huffman tree from a frequency map. - + Args: frequencies: A dictionary mapping bytes to their frequencies - + Returns: The root node of the Huffman tree - + Raises: RuntimeError: If the frequency map is empty )pbdoc"); @@ -116,13 +116,13 @@ PYBIND11_MODULE(huffman, m) { py::arg("root"), R"pbdoc( Generate a mapping of bytes to their Huffman codes. - + Args: root: The root node of the Huffman tree - + Returns: A dictionary mapping bytes to their Huffman codes (as strings of '0's and '1's) - + Raises: RuntimeError: If the root node is null )pbdoc"); @@ -131,14 +131,14 @@ PYBIND11_MODULE(huffman, m) { py::arg("huffman_codes"), R"pbdoc( Compress data using Huffman codes. - + Args: data: The data to compress as a bytes-like object huffman_codes: A dictionary mapping bytes to Huffman codes - + Returns: A string of '0's and '1's representing the compressed data - + Raises: RuntimeError: If a byte in the data doesn't have a corresponding Huffman code )pbdoc"); @@ -147,14 +147,14 @@ PYBIND11_MODULE(huffman, m) { py::arg("compressed_data"), py::arg("root"), R"pbdoc( Decompress Huffman-encoded data. - + Args: compressed_data: The compressed data as a string of '0's and '1's root: The root node of the Huffman tree - + Returns: The decompressed data as bytes - + Raises: RuntimeError: If the compressed data is invalid or the tree is null )pbdoc"); @@ -162,10 +162,10 @@ PYBIND11_MODULE(huffman, m) { m.def("serialize_tree", &atom::algorithm::serializeTree, py::arg("root"), R"pbdoc( Serialize a Huffman tree to a binary string. - + Args: root: The root node of the Huffman tree - + Returns: A string of '0's and '1's representing the serialized tree )pbdoc"); @@ -179,13 +179,13 @@ PYBIND11_MODULE(huffman, m) { py::arg("serialized_tree"), R"pbdoc( Deserialize a binary string back into a Huffman tree. - + Args: serialized_tree: The serialized tree as a string of '0's and '1's - + Returns: The root node of the reconstructed Huffman tree - + Raises: RuntimeError: If the serialized tree format is invalid )pbdoc"); @@ -194,11 +194,11 @@ PYBIND11_MODULE(huffman, m) { py::arg("root"), py::arg("indent") = "", R"pbdoc( Print a visualization of a Huffman tree. - + Args: root: The root node of the Huffman tree indent: The indentation to use (mostly for internal recursion) - + Note: This function prints to standard output and doesn't return anything. )pbdoc"); @@ -238,15 +238,15 @@ PYBIND11_MODULE(huffman, m) { py::arg("data"), R"pbdoc( Compress data using Huffman encoding. - + Args: data: The data to compress as a bytes-like object - + Returns: A tuple of (compressed_data, serialized_tree) where: - compressed_data: A string of '0's and '1's representing the compressed data - serialized_tree: A string of '0's and '1's representing the serialized Huffman tree - + Raises: RuntimeError: If compression fails )pbdoc"); @@ -271,14 +271,14 @@ PYBIND11_MODULE(huffman, m) { py::arg("compressed_data"), py::arg("serialized_tree"), R"pbdoc( Decompress Huffman-encoded data. - + Args: compressed_data: The compressed data as a string of '0's and '1's serialized_tree: The serialized Huffman tree as a string of '0's and '1's - + Returns: The decompressed data as bytes - + Raises: RuntimeError: If decompression fails )pbdoc"); @@ -348,13 +348,13 @@ PYBIND11_MODULE(huffman, m) { py::arg("data"), R"pbdoc( Compress data using Huffman encoding and pack everything into a single binary format. - + Args: data: The data to compress as a bytes-like object - + Returns: A bytes object containing the compressed data and Huffman tree - + Raises: RuntimeError: If compression fails )pbdoc"); @@ -427,13 +427,13 @@ PYBIND11_MODULE(huffman, m) { py::arg("encoded_data"), R"pbdoc( Decompress data that was compressed with the encode() function. - + Args: encoded_data: The encoded data as returned by encode() - + Returns: The original decompressed data as bytes - + Raises: ValueError: If the encoded data format is invalid RuntimeError: If decompression fails @@ -453,10 +453,10 @@ PYBIND11_MODULE(huffman, m) { py::arg("data"), R"pbdoc( Calculate the frequency of each byte in the data. - + Args: data: The data as a bytes-like object - + Returns: A dictionary mapping bytes to their frequencies )pbdoc"); @@ -473,11 +473,11 @@ PYBIND11_MODULE(huffman, m) { py::arg("original_data"), py::arg("compressed_bit_string"), R"pbdoc( Calculate the compression ratio (compressed size / original size). - + Args: original_data: The original uncompressed data compressed_bit_string: The compressed data as a string of '0's and '1's - + Returns: The compression ratio as a float (smaller is better) )pbdoc"); @@ -500,10 +500,10 @@ PYBIND11_MODULE(huffman, m) { py::arg("bit_string"), R"pbdoc( Convert a string of '0's and '1's to bytes. - + Args: bit_string: A string of '0's and '1's - + Returns: The packed bytes )pbdoc"); @@ -527,11 +527,11 @@ PYBIND11_MODULE(huffman, m) { py::arg("data"), py::arg("bit_count"), R"pbdoc( Convert bytes to a string of '0's and '1's. - + Args: data: The bytes to convert bit_count: The number of bits to extract - + Returns: A string of '0's and '1's )pbdoc"); @@ -577,11 +577,11 @@ PYBIND11_MODULE(huffman, m) { py::arg("codes"), R"pbdoc( Analyze the properties of a set of Huffman codes. - + Args: codes: A dictionary mapping bytes to Huffman codes - + Returns: A dictionary containing statistics about the codes )pbdoc"); -} \ No newline at end of file +} diff --git a/python/algorithm/md5.cpp b/python/algorithm/md5.cpp index 6eba9941..f881baf9 100644 --- a/python/algorithm/md5.cpp +++ b/python/algorithm/md5.cpp @@ -14,19 +14,19 @@ PYBIND11_MODULE(md5, m) { This module provides a modern, optimized implementation of the MD5 hashing algorithm with additional utility functions and binary data support. - + Example: >>> from atom.algorithm import md5 - >>> + >>> >>> # Compute MD5 hash of a string >>> hash_value = md5.encrypt("Hello, world!") >>> print(hash_value) '6cd3556deb0da54bca060b4c39479839' - + >>> # Verify a hash >>> md5.verify("Hello, world!", hash_value) True - + >>> # Compute hash of binary data >>> import os >>> binary_data = os.urandom(1024) @@ -42,13 +42,13 @@ PYBIND11_MODULE(md5, m) { py::arg("input"), R"pbdoc( Encrypts a string using the MD5 algorithm. - + Args: input: The input string to hash - + Returns: The MD5 hash as a lowercase hex string - + Raises: MD5Exception: If encryption fails )pbdoc"); @@ -57,11 +57,11 @@ PYBIND11_MODULE(md5, m) { py::arg("input"), py::arg("hash"), R"pbdoc( Verifies if a string matches a given MD5 hash. - + Args: input: The input string to check hash: The expected MD5 hash - + Returns: True if the hash of input matches the expected hash, False otherwise )pbdoc"); @@ -83,17 +83,17 @@ PYBIND11_MODULE(md5, m) { py::arg("data"), R"pbdoc( Computes MD5 hash for binary data. - + Args: data: Binary data (bytes, bytearray, or any buffer-like object) - + Returns: The MD5 hash as a lowercase hex string - + Raises: ValueError: If encryption fails )pbdoc"); // Add version information m.attr("__version__") = "1.0.0"; -} \ No newline at end of file +} diff --git a/python/algorithm/mhash.cpp b/python/algorithm/mhash.cpp index 21f40ba6..fe08b65e 100644 --- a/python/algorithm/mhash.cpp +++ b/python/algorithm/mhash.cpp @@ -25,21 +25,21 @@ PYBIND11_MODULE(mhash, m) { Optimized Hashing Algorithms --------------------------- - This module provides implementation of MinHash for similarity estimation + This module provides implementation of MinHash for similarity estimation and Keccak-256 cryptographic hash functions. - + The module includes: - MinHash implementation for estimating Jaccard similarity between sets - Keccak-256 cryptographic hash function (compatible with Ethereum's keccak256) - Utility functions for hex string conversion - + Example: >>> from atom.algorithm import mhash - >>> + >>> >>> # Computing Keccak-256 hash >>> h = mhash.keccak256("Hello, world!") >>> print(mhash.hash_to_hex(h)) - + >>> # Using MinHash for similarity estimation >>> minhash = mhash.MinHash(100) # 100 hash functions >>> sig1 = minhash.compute_signature(["a", "b", "c", "d"]) @@ -238,4 +238,4 @@ Jaccard index (similarity) between sets based on these signatures. // Constants m.attr("HASH_SIZE") = atom::algorithm::K_HASH_SIZE; -} \ No newline at end of file +} diff --git a/python/algorithm/pathfinding.cpp b/python/algorithm/pathfinding.cpp index 437754ed..e70f21a4 100644 --- a/python/algorithm/pathfinding.cpp +++ b/python/algorithm/pathfinding.cpp @@ -292,4 +292,4 @@ This combines Manhattan distance with diagonal shortcuts. ... ] >>> path = find_path_with_obstacles(obstacles, Point(0, 0), Point(4, 4)) )"); -} \ No newline at end of file +} diff --git a/python/algorithm/perlin.cpp b/python/algorithm/perlin.cpp index e1362f7f..d6fd0d7d 100644 --- a/python/algorithm/perlin.cpp +++ b/python/algorithm/perlin.cpp @@ -14,22 +14,22 @@ PYBIND11_MODULE(perlin, m) { This module provides a high-performance implementation of Perlin noise, with support for multiple octaves, persistence, and GPU acceleration. - + Features: - 1D, 2D, and 3D noise generation - Octave noise for more natural patterns - Noise map generation for terrain or texture creation - OpenCL acceleration when available - + Example: >>> from atom.algorithm.perlin import PerlinNoise - >>> + >>> >>> # Create a noise generator with a specific seed >>> noise = PerlinNoise(seed=42) - >>> + >>> >>> # Generate a single noise value >>> value = noise.noise(1.0, 2.0, 0.5) - >>> + >>> >>> # Generate a 2D noise map (e.g., for terrain) >>> noise_map = noise.generate_noise_map(256, 256, scale=25.0, octaves=4, persistence=0.5) )pbdoc"; @@ -52,14 +52,14 @@ PYBIND11_MODULE(perlin, m) { py::class_(m, "PerlinNoise", R"pbdoc( Perlin noise generator class. - + This class implements the improved Perlin noise algorithm for generating coherent noise in 1D, 2D, or 3D space. It can be used for procedural generation of terrain, textures, animations, etc. - + Constructor Args: seed: Optional random seed for noise generation (default: system random) - + Examples: >>> noise = PerlinNoise(seed=42) >>> value = noise.noise(x=1.0, y=2.0, z=3.0) @@ -75,15 +75,15 @@ PYBIND11_MODULE(perlin, m) { py::arg("x"), py::arg("y"), py::arg("z"), R"pbdoc( Generate a 3D Perlin noise value. - + Args: x: X-coordinate in noise space y: Y-coordinate in noise space z: Z-coordinate in noise space - + Returns: Noise value in range [0.0, 1.0] - + Example: >>> noise = PerlinNoise(seed=42) >>> value = noise.noise(0.5, 1.2, 0.8) @@ -97,14 +97,14 @@ PYBIND11_MODULE(perlin, m) { py::arg("x"), py::arg("y"), R"pbdoc( Generate a 2D Perlin noise value. - + Args: x: X-coordinate in noise space y: Y-coordinate in noise space - + Returns: Noise value in range [0.0, 1.0] - + Example: >>> noise = PerlinNoise(seed=42) >>> value = noise.noise_2d(0.5, 1.2) @@ -118,13 +118,13 @@ PYBIND11_MODULE(perlin, m) { py::arg("x"), R"pbdoc( Generate a 1D Perlin noise value. - + Args: x: X-coordinate in noise space - + Returns: Noise value in range [0.0, 1.0] - + Example: >>> noise = PerlinNoise(seed=42) >>> value = noise.noise_1d(0.5) @@ -139,17 +139,17 @@ PYBIND11_MODULE(perlin, m) { py::arg("persistence"), R"pbdoc( Generate fractal noise by summing multiple octaves of Perlin noise. - + Args: x: X-coordinate in noise space y: Y-coordinate in noise space z: Z-coordinate in noise space octaves: Number of noise layers to sum persistence: Amplitude multiplier for each octave (0.0-1.0) - + Returns: Octave noise value in range [0.0, 1.0] - + Example: >>> noise = PerlinNoise(seed=42) >>> value = noise.octave_noise(0.5, 1.2, 0.8, octaves=4, persistence=0.5) @@ -164,13 +164,13 @@ PYBIND11_MODULE(perlin, m) { py::arg("persistence"), R"pbdoc( Generate 2D fractal noise by summing multiple octaves of Perlin noise. - + Args: x: X-coordinate in noise space y: Y-coordinate in noise space octaves: Number of noise layers to sum persistence: Amplitude multiplier for each octave (0.0-1.0) - + Returns: Octave noise value in range [0.0, 1.0] )pbdoc") @@ -202,9 +202,9 @@ PYBIND11_MODULE(perlin, m) { py::arg("seed") = std::default_random_engine::default_seed, R"pbdoc( Generate a 2D noise map. - + This is useful for terrain generation, textures, or other 2D applications. - + Args: width: Width of the noise map height: Height of the noise map @@ -213,17 +213,17 @@ PYBIND11_MODULE(perlin, m) { persistence: Amplitude reduction per octave (0.0-1.0) lacunarity: Frequency multiplier per octave (default: 2.0) seed: Random seed for noise map generation (default: uses object's seed) - + Returns: 2D numpy array of noise values in range [0.0, 1.0] - + Example: >>> noise = PerlinNoise(seed=42) >>> terrain = noise.generate_noise_map( - ... width=256, height=256, + ... width=256, height=256, ... scale=50.0, octaves=4, persistence=0.5 ... ) - >>> + >>> >>> # You can visualize it with matplotlib: >>> import matplotlib.pyplot as plt >>> plt.imshow(terrain, cmap='terrain') @@ -245,7 +245,7 @@ PYBIND11_MODULE(perlin, m) { py::arg("seed") = std::default_random_engine::default_seed, R"pbdoc( Convenience function to create a fractal noise map in one call. - + Args: width: Width of the noise map height: Height of the noise map @@ -254,7 +254,7 @@ PYBIND11_MODULE(perlin, m) { persistence: Amplitude reduction per octave (0.0-1.0) lacunarity: Frequency multiplier per octave (default: 2.0) seed: Random seed for noise map generation - + Returns: 2D numpy array of noise values in range [0.0, 1.0] )pbdoc"); @@ -268,4 +268,4 @@ PYBIND11_MODULE(perlin, m) { // Add version information m.attr("__version__") = "1.0.0"; -} \ No newline at end of file +} diff --git a/python/algorithm/rust_numeric.cpp b/python/algorithm/rust_numeric.cpp index 4bf9db46..b89317a8 100644 --- a/python/algorithm/rust_numeric.cpp +++ b/python/algorithm/rust_numeric.cpp @@ -357,7 +357,7 @@ PYBIND11_MODULE(rust_numeric, m) { m.attr("__doc__") = R"( Rust-like numeric types and utilities for Python - This module provides Rust-inspired numeric types and operations with + This module provides Rust-inspired numeric types and operations with controlled overflow behavior, checked arithmetic, and error handling patterns. Examples: @@ -369,7 +369,7 @@ PYBIND11_MODULE(rust_numeric, m) { >>> else: >>> print(result.unwrap_err()) 123 - + >>> # Check for overflow when adding >>> option = I32.checked_add(2147483647, 1) # MAX_INT32 + 1 >>> if option.is_some(): @@ -377,12 +377,12 @@ PYBIND11_MODULE(rust_numeric, m) { >>> else: >>> print("Overflow occurred") Overflow occurred - + >>> # Saturating operations (clamping to min/max) >>> saturated = I32.saturating_add(2147483647, 1000) >>> print(saturated) 2147483647 - + >>> # Working with ranges >>> from atom.algorithm.rust_numeric import range, range_inclusive >>> r = range(1, 5) # 1, 2, 3, 4 @@ -393,4 +393,4 @@ PYBIND11_MODULE(rust_numeric, m) { 3 4 )"; -} \ No newline at end of file +} diff --git a/python/algorithm/sha1.cpp b/python/algorithm/sha1.cpp index 0c813939..2fd3f8a6 100644 --- a/python/algorithm/sha1.cpp +++ b/python/algorithm/sha1.cpp @@ -19,31 +19,31 @@ PYBIND11_MODULE(sha1, m) { -------------------------------------- This module provides a SHA-1 hash implementation conforming to FIPS PUB 180-4. - + The SHA1 class allows incremental updates to compute the hash of large data, and supports both raw byte arrays and higher-level containers as input. - + Note: While SHA-1 is no longer considered secure for cryptographic purposes, it remains useful for non-security applications like data integrity checks. Example: >>> from atom.algorithm import sha1 - >>> + >>> >>> # Create a new SHA1 hash object >>> hasher = sha1.SHA1() - >>> + >>> >>> # Update with data >>> hasher.update(b"Hello") >>> hasher.update(b", World!") - >>> + >>> >>> # Get digest as bytes >>> digest_bytes = hasher.digest_bytes() >>> print(digest_bytes.hex()) - >>> + >>> >>> # Or as a hex string >>> digest_str = hasher.digest_string() >>> print(digest_str) - >>> + >>> >>> # One-step hashing convenience function >>> hash_value = sha1.compute_hash("Hello, World!") )pbdoc"; @@ -72,18 +72,18 @@ It supports incremental updates, allowing the hash of large data to be computed Examples: >>> from atom.algorithm.sha1 import SHA1 - >>> + >>> >>> # Create a new hash object >>> hasher = SHA1() - >>> + >>> >>> # Update with data incrementally >>> hasher.update(b"Hello") >>> hasher.update(b", World!") - >>> + >>> >>> # Get the digest as a hexadecimal string >>> digest = hasher.digest_string() >>> print(digest) - >>> + >>> >>> # Reset and start a new hash >>> hasher.reset() >>> hasher.update(b"New data") @@ -369,4 +369,4 @@ for hashing new data. >>> # Print the hash of the first item >>> print(bytes_to_hex(hashes[0])) )"); -} \ No newline at end of file +} diff --git a/python/algorithm/snowflake.cpp b/python/algorithm/snowflake.cpp index cc453462..d6aad236 100644 --- a/python/algorithm/snowflake.cpp +++ b/python/algorithm/snowflake.cpp @@ -25,32 +25,32 @@ PYBIND11_MODULE(snowflake, m) { ----------------------- This module provides a distributed ID generator based on Twitter's Snowflake algorithm. - + The Snowflake algorithm generates 64-bit unique IDs that are: - Time-based (roughly sortable by generation time) - Distributed (different workers/datacenter IDs produce different ranges) - High-performance (can generate thousands of IDs per second per node) - + The generated IDs are composed of: - Timestamp (milliseconds since a custom epoch) - Datacenter ID (5 bits) - Worker ID (5 bits) - Sequence number (12 bits, for multiple IDs in the same millisecond) - + Example: >>> from atom.algorithm import snowflake - >>> + >>> >>> # Create a generator with worker_id=1, datacenter_id=2 >>> generator = snowflake.SnowflakeGenerator(1, 2) - >>> + >>> >>> # Generate a single ID >>> id = generator.next_id() >>> print(id) - + >>> # Generate multiple IDs at once >>> ids = generator.next_ids(5) # Generate 5 IDs >>> print(ids) - + >>> # Extract timestamp from an ID >>> timestamp = generator.extract_timestamp(id) >>> print(timestamp) @@ -304,4 +304,4 @@ The Snowflake algorithm generates 64-bit IDs composed of: atom::algorithm::Snowflake::SEQUENCE_BITS; m.attr("TWEPOCH") = atom::algorithm::Snowflake::TWEPOCH; -} \ No newline at end of file +} diff --git a/python/algorithm/tea.cpp b/python/algorithm/tea.cpp index 8462b42c..5de706e6 100644 --- a/python/algorithm/tea.cpp +++ b/python/algorithm/tea.cpp @@ -469,4 +469,4 @@ This is a convenience function that handles conversion between byte data and 32- >>> print(key) >>> encrypted = encrypt_bytes(b"Secret message", key) )"); -} \ No newline at end of file +} diff --git a/python/algorithm/weight.cpp b/python/algorithm/weight.cpp index 6fb97f95..1ad131fa 100644 --- a/python/algorithm/weight.cpp +++ b/python/algorithm/weight.cpp @@ -80,15 +80,15 @@ This class provides methods for weighted random selection with different probabi Examples: >>> from atom.algorithm.weight import WeightSelectorFloat, SelectionStrategyFloat - >>> + >>> >>> # Create a selector with default strategy >>> selector = WeightSelectorFloat([1.0, 2.0, 3.0, 4.0]) - >>> + >>> >>> # Select an index based on weights >>> selected_index = selector.select() >>> >>> # Use bottom-heavy distribution (favors lower weights) - >>> selector2 = WeightSelectorFloat([1.0, 2.0, 3.0, 4.0], + >>> selector2 = WeightSelectorFloat([1.0, 2.0, 3.0, 4.0], >>> strategy=SelectionStrategyFloat.BOTTOM_HEAVY) >>> >>> # Multiple selections without replacement @@ -192,7 +192,7 @@ This class provides methods for weighted random selection with different probabi Raises: ValueError: If resulting weights are negative - + Examples: >>> # Double all weights >>> selector.apply_function_to_weights(lambda w: w * 2) @@ -372,15 +372,15 @@ This class provides methods for weighted random sampling with or without replace Args: seed: Optional random seed for reproducible sampling - + Examples: >>> from atom.algorithm.weight import WeightedRandomSamplerFloat - >>> + >>> >>> sampler = WeightedRandomSamplerFloat(seed=42) - >>> + >>> >>> # Sample 3 indices with replacement >>> indices1 = sampler.sample([1.0, 2.0, 3.0, 4.0], 3) - >>> + >>> >>> # Sample 2 unique indices (no replacement) >>> indices2 = sampler.sample_unique([1.0, 2.0, 3.0, 4.0], 2) )") @@ -395,7 +395,7 @@ This class provides methods for weighted random sampling with or without replace Returns: List of sampled indices - + Raises: ValueError: If weights is empty )") @@ -410,7 +410,7 @@ This class provides methods for weighted random sampling with or without replace Returns: List of sampled indices - + Raises: ValueError: If n is greater than the number of weights or if weights is empty )"); @@ -444,32 +444,32 @@ PYBIND11_MODULE(weight, m) { Weighted Random Selection Algorithms ----------------------------------- - This module provides flexible weighted random selection algorithms with + This module provides flexible weighted random selection algorithms with multiple probability distributions and thread-safe operations. - + The module includes: - Various selection strategies (uniform, bottom-heavy, top-heavy, etc.) - Methods for selecting with and without replacement - Thread-safe weight updates and manipulations - Utilities for normalizing and transforming weights - Detailed statistics and weight information - + Example: >>> from atom.algorithm import weight - >>> + >>> >>> # Create a selector with weights >>> selector = weight.WeightSelectorFloat([1.0, 2.0, 3.0, 4.0]) - >>> + >>> >>> # Select an index based on weights >>> selected_idx = selector.select() >>> print(selected_idx) - + >>> # Select using a bottom-heavy distribution >>> selector2 = weight.WeightSelectorFloat( - >>> [1.0, 2.0, 3.0, 4.0], + >>> [1.0, 2.0, 3.0, 4.0], >>> strategy=weight.SelectionStrategyFloat.BOTTOM_HEAVY >>> ) - >>> + >>> >>> # Select multiple unique indices >>> indices = selector.select_unique_multiple(2) )pbdoc"; @@ -551,7 +551,7 @@ The function automatically selects the WeightSelector type based on the input we Examples: >>> # Create a selector with integer weights >>> int_selector = weight.create_selector([1, 2, 3, 4]) - >>> + >>> >>> # Create a selector with float weights and power law distribution >>> float_selector = weight.create_selector( >>> [1.0, 2.0, 3.0, 4.0], @@ -559,4 +559,4 @@ The function automatically selects the WeightSelector type based on the input we >>> exponent=1.5 >>> ) )"); -} \ No newline at end of file +} diff --git a/python/async/__init__.py b/python/async/__init__.py index 879c3789..ce38ef10 100644 --- a/python/async/__init__.py +++ b/python/async/__init__.py @@ -1 +1 @@ -# Auto-generated __init__.py for async module \ No newline at end of file +# Auto-generated __init__.py for async module diff --git a/python/async/async.cpp b/python/async/async.cpp index 2af85e5a..c469b9f6 100644 --- a/python/async/async.cpp +++ b/python/async/async.cpp @@ -297,10 +297,10 @@ void declare_async_retry(py::module& m, const std::string& suffix) { callback: Callback function called on success (default: no-op) exception_handler: Handler called when exceptions occur (default: no-op) complete_handler: Handler called when all attempts complete (default: no-op) - + Returns: A future with the result of the async operation - + Raises: ValueError: If invalid parameters are provided )pbdoc"); @@ -311,36 +311,36 @@ PYBIND11_MODULE(async, m) { Asynchronous Task Processing Module ---------------------------------- - This module provides tools for executing tasks asynchronously with + This module provides tools for executing tasks asynchronously with features like timeouts, callbacks, and task management. - + Key components: - AsyncWorker: Manages a single asynchronous task - AsyncWorkerManager: Coordinates multiple async workers - Task/Future wrappers: Enhanced futures with additional capabilities - Retry mechanisms: Automatic retry with configurable backoff strategies - + Example: >>> from atom.async import AsyncWorkerInt, AsyncWorkerManagerInt - >>> + >>> >>> # Create a worker and start a task >>> worker = AsyncWorkerInt() >>> worker.start_async(lambda: 42) - >>> + >>> >>> # Get the result (with optional timeout) >>> result = worker.get_result(timeout=5000) # 5 seconds timeout >>> print(result) # Output: 42 - >>> + >>> >>> # Create a worker manager for multiple tasks >>> manager = AsyncWorkerManagerInt() >>> workers = [ - >>> manager.create_worker(lambda: i * 10) + >>> manager.create_worker(lambda: i * 10) >>> for i in range(5) >>> ] - >>> + >>> >>> # Wait for all tasks to complete >>> manager.wait_for_all() - >>> + >>> >>> # Collect results >>> results = [w.get_result() for w in workers] >>> print(results) # Output: [0, 10, 20, 30, 40] @@ -417,14 +417,14 @@ PYBIND11_MODULE(async, m) { py::arg("future"), py::arg("timeout"), R"pbdoc( Gets the result of a future with a timeout. - + Args: future: The future to get the result from timeout: The timeout in seconds - + Returns: The result of the future - + Raises: TimeoutException: If the timeout is reached )pbdoc"); @@ -438,4 +438,4 @@ PYBIND11_MODULE(async, m) { // Add version information m.attr("__version__") = "1.0.0"; -} \ No newline at end of file +} diff --git a/python/async/async_executor.cpp b/python/async/async_executor.cpp index c346fd79..86f785eb 100644 --- a/python/async/async_executor.cpp +++ b/python/async/async_executor.cpp @@ -11,30 +11,30 @@ PYBIND11_MODULE(async_executor, m) { Advanced Async Task Executor --------------------------- - This module provides a high-performance asynchronous task executor with + This module provides a high-performance asynchronous task executor with thread pooling, priority-based scheduling, and multiple execution strategies. - + The module includes: - Thread pool with dynamic resizing - Priority-based task scheduling (LOW, NORMAL, HIGH, CRITICAL) - Various execution strategies (IMMEDIATE, DEFERRED, SCHEDULED) - Task cancellation support - Wait for completion functionality - + Example: >>> from atom.async.async_executor import AsyncExecutor, ExecutionStrategy, TaskPriority - >>> + >>> >>> # Create an executor with 4 threads >>> executor = AsyncExecutor(4) - >>> + >>> >>> # Schedule a task for immediate execution with normal priority >>> future = executor.schedule( - >>> ExecutionStrategy.IMMEDIATE, + >>> ExecutionStrategy.IMMEDIATE, >>> TaskPriority.NORMAL, - >>> lambda x: x * 2, + >>> lambda x: x * 2, >>> 10 >>> ) - >>> + >>> >>> # Get the result when ready >>> result = future.result() >>> print(result) # Outputs: 20 @@ -110,12 +110,12 @@ with different execution strategies and priorities. Examples: >>> executor = AsyncExecutor(4) # Create an executor with 4 threads - >>> + >>> >>> # Schedule an immediate task >>> future = executor.schedule( - >>> ExecutionStrategy.IMMEDIATE, + >>> ExecutionStrategy.IMMEDIATE, >>> TaskPriority.NORMAL, - >>> lambda x: x * 2, + >>> lambda x: x * 2, >>> 10 >>> ) >>> @@ -205,4 +205,4 @@ with different execution strategies and priorities. // Add version information m.attr("__version__") = "1.0.0"; -} \ No newline at end of file +} diff --git a/python/async/daemon.cpp b/python/async/daemon.cpp index bfcd8b05..bfccccfe 100644 --- a/python/async/daemon.cpp +++ b/python/async/daemon.cpp @@ -15,27 +15,27 @@ PYBIND11_MODULE(daemon, m) { This module provides tools for creating and managing daemon processes on both Unix-like systems and Windows. - + Features: - Create daemon processes that run in the background - Monitor and control daemon processes - Handle daemon restarts and failure recovery - Manage daemon PID files - + Example: >>> from atom.async.daemon import DaemonGuard, check_pid_file, write_pid_file - >>> + >>> >>> # Check if daemon is already running >>> if not check_pid_file("my-daemon"): >>> # Create a daemon process >>> daemon = DaemonGuard() - >>> + >>> >>> # Define the main process function >>> def main_process(argc, argv): >>> # Your daemon code here >>> write_pid_file("my-daemon") >>> return 0 - >>> + >>> >>> # Start the daemon >>> daemon.start_daemon(0, [], main_process, True) )pbdoc"; @@ -65,18 +65,18 @@ PYBIND11_MODULE(daemon, m) { py::class_(m, "DaemonGuard", R"pbdoc( Class for managing daemon processes. - + This class provides methods to start, monitor and control daemon processes on both Unix-like systems and Windows. - + Examples: >>> daemon = DaemonGuard() - >>> + >>> >>> # Define the main process function >>> def main_process(argc, argv): >>> # Your daemon code here >>> return 0 - >>> + >>> >>> # Start a daemon process >>> daemon.start_daemon(0, [], main_process, True) )pbdoc") @@ -112,15 +112,15 @@ PYBIND11_MODULE(daemon, m) { py::arg("argc"), py::arg("argv"), py::arg("main_cb"), R"pbdoc( Starts a child process to execute the actual task. - + Args: argc: The number of command line arguments argv: A list of command line arguments main_cb: The main callback function to be executed in the child process - + Returns: The return value of the main callback function - + Raises: DaemonException: If process creation fails )pbdoc") @@ -151,15 +151,15 @@ PYBIND11_MODULE(daemon, m) { py::arg("argc"), py::arg("argv"), py::arg("main_cb"), R"pbdoc( Starts a child process as a daemon to execute the actual task. - + Args: argc: The number of command line arguments argv: A list of command line arguments main_cb: The main callback function to be executed in the daemon process - + Returns: The return value of the main callback function - + Raises: DaemonException: If daemon process creation fails )pbdoc") @@ -192,18 +192,18 @@ PYBIND11_MODULE(daemon, m) { py::arg("argc"), py::arg("argv"), py::arg("main_cb"), py::arg("is_daemon"), R"pbdoc( - Starts the process. If a daemon process needs to be created, + Starts the process. If a daemon process needs to be created, it will create the daemon process first. - + Args: argc: The number of command line arguments argv: A list of command line arguments main_cb: The main callback function to be executed is_daemon: Determines if a daemon process should be created - + Returns: The return value of the main callback function - + Raises: DaemonException: If process creation fails )pbdoc") @@ -224,10 +224,10 @@ PYBIND11_MODULE(daemon, m) { py::arg("file_path") = "lithium-daemon", R"pbdoc( Writes the process ID to a file. - + Args: file_path: Path to write the PID file (default: "lithium-daemon") - + Raises: OSError: If file operation fails )pbdoc"); @@ -240,10 +240,10 @@ PYBIND11_MODULE(daemon, m) { py::arg("file_path") = "lithium-daemon", R"pbdoc( Checks if the process ID file exists and the process is running. - + Args: file_path: Path to the PID file (default: "lithium-daemon") - + Returns: True if the PID file exists and the process is running, False otherwise )pbdoc"); @@ -252,10 +252,10 @@ PYBIND11_MODULE(daemon, m) { py::arg("seconds"), R"pbdoc( Sets the restart interval for daemon processes. - + Args: seconds: Interval in seconds - + Raises: ValueError: If seconds is less than or equal to zero )pbdoc"); @@ -272,4 +272,4 @@ PYBIND11_MODULE(daemon, m) { // Add version information m.attr("__version__") = "1.0.0"; -} \ No newline at end of file +} diff --git a/python/async/eventstack.cpp b/python/async/eventstack.cpp index acc48a05..857189ff 100644 --- a/python/async/eventstack.cpp +++ b/python/async/eventstack.cpp @@ -24,26 +24,26 @@ void declare_event_stack(py::module& m, const std::string& type_name) { Args: None (default constructor) - + Examples: >>> from atom.async.eventstack import EventStackInt - >>> + >>> >>> # Create an event stack >>> stack = EventStackInt() - >>> + >>> >>> # Add some events >>> stack.push_event(42) >>> stack.push_event(100) >>> stack.push_event(7) - >>> + >>> >>> # Access the top event without removing it >>> top = stack.peek_top_event() >>> print(top) # Output: 7 - >>> + >>> >>> # Pop an event >>> event = stack.pop_event() >>> print(event) # Output: 7 - >>> + >>> >>> # Check size >>> print(len(stack)) # Output: 2 )pbdoc") @@ -55,35 +55,35 @@ void declare_event_stack(py::module& m, const std::string& type_name) { /* .def(py::init(), py::arg("other"), "Move constructor - creates a new stack by taking ownership of " - "another stack") + "another stack") */ - + // Core stack operations .def("push_event", &EventStackType::pushEvent, py::arg("event"), R"pbdoc( Pushes an event onto the stack. - + Args: event: The event to push onto the stack - + Raises: RuntimeError: If memory allocation fails )pbdoc") .def("pop_event", &EventStackType::popEvent, R"pbdoc( Pops an event from the stack. - + Returns: The popped event, or None if the stack is empty )pbdoc") .def("peek_top_event", &EventStackType::peekTopEvent, R"pbdoc( Returns the top event in the stack without removing it. - + Returns: The top event, or None if the stack is empty - + Raises: RuntimeError: If the stack is empty and exceptions are enabled )pbdoc") @@ -110,11 +110,11 @@ void declare_event_stack(py::module& m, const std::string& type_name) { py::arg("predicate"), R"pbdoc( Filters events based on a custom filter function. - + Args: predicate: A function that takes an event and returns a boolean. Events are kept if the function returns True. - + Examples: >>> # Keep only events greater than 50 >>> stack.filter_events(lambda event: event > 50) @@ -130,13 +130,13 @@ void declare_event_stack(py::module& m, const std::string& type_name) { py::arg("predicate"), R"pbdoc( Finds the first event that satisfies a predicate. - + Args: predicate: A function that takes an event and returns a boolean - + Returns: The first event satisfying the predicate, or None if not found - + Examples: >>> # Find first event divisible by 10 >>> event = stack.find_event(lambda e: e % 10 == 0) @@ -154,13 +154,13 @@ void declare_event_stack(py::module& m, const std::string& type_name) { py::arg("predicate"), R"pbdoc( Counts the number of events that satisfy a predicate. - + Args: predicate: A function that takes an event and returns a boolean - + Returns: The count of events satisfying the predicate - + Examples: >>> # Count events less than 100 >>> count = stack.count_events(lambda e: e < 100) @@ -176,10 +176,10 @@ void declare_event_stack(py::module& m, const std::string& type_name) { py::arg("predicate"), R"pbdoc( Checks if any event in the stack satisfies a predicate. - + Args: predicate: A function that takes an event and returns a boolean - + Returns: True if any event satisfies the predicate, False otherwise )pbdoc") @@ -194,10 +194,10 @@ void declare_event_stack(py::module& m, const std::string& type_name) { py::arg("predicate"), R"pbdoc( Checks if all events in the stack satisfy a predicate. - + Args: predicate: A function that takes an event and returns a boolean - + Returns: True if all events satisfy the predicate, False otherwise )pbdoc") @@ -217,10 +217,10 @@ void declare_event_stack(py::module& m, const std::string& type_name) { py::arg("transform_func"), R"pbdoc( Transforms events using the provided function. - + Args: transform_func: A function that takes an event and returns a new event or None - + Examples: >>> # Double all event values >>> stack.transform_events(lambda e: e * 2) @@ -236,11 +236,11 @@ void declare_event_stack(py::module& m, const std::string& type_name) { py::arg("compare_func"), R"pbdoc( Sorts the events in the stack based on a custom comparison function. - + Args: compare_func: A function that takes two events and returns a boolean. Returns true if the first argument should be placed before the second. - + Examples: >>> # Sort in descending order >>> stack.sort_events(lambda a, b: a > b) @@ -260,10 +260,10 @@ void declare_event_stack(py::module& m, const std::string& type_name) { py::arg("func"), R"pbdoc( Applies a function to each event in the stack. - + Args: func: A function to apply to each event - + Examples: >>> # Print each event >>> stack.for_each(lambda e: print(e)) @@ -293,7 +293,7 @@ PYBIND11_MODULE(eventstack, m) { This module provides a thread-safe stack data structure for managing events with support for various filtering, transformation, and querying operations. - + Features: - Thread-safe event storage with LIFO (Last In, First Out) semantics - Atomic operations for push, pop, and peek @@ -301,32 +301,32 @@ PYBIND11_MODULE(eventstack, m) { - Search functionality - Statistical queries - Support for various data types - + The module includes implementations for common data types: - EventStackInt: For integer events - - EventStackFloat: For floating-point events + - EventStackFloat: For floating-point events - EventStackString: For string events - EventStackBool: For boolean events - + Example: >>> from atom.async.eventstack import EventStackInt - >>> + >>> >>> # Create an event stack >>> stack = EventStackInt() - >>> + >>> >>> # Add events >>> for i in range(10): >>> stack.push_event(i) - >>> + >>> >>> # Check if any event satisfies a condition >>> has_even = stack.any_event(lambda e: e % 2 == 0) >>> print(f"Has even numbers: {has_even}") - >>> + >>> >>> # Find an event >>> five = stack.find_event(lambda e: e == 5) >>> if five is not None: >>> print(f"Found: {five}") - >>> + >>> >>> # Filter events >>> stack.filter_events(lambda e: e > 5) >>> print(f"Events after filtering: {stack.size()}") @@ -386,22 +386,22 @@ PYBIND11_MODULE(eventstack, m) { py::arg("sample_event"), R"pbdoc( Factory function to create an appropriate EventStack based on the input type. - + Args: sample_event: An example event of the type you want to store (used only to determine the type) - + Returns: A new EventStack of the appropriate type - + Raises: TypeError: If the event type is not supported - + Examples: >>> # Create an integer event stack >>> int_stack = create_event_stack(42) - >>> - >>> # Create a string event stack + >>> + >>> # Create a string event stack >>> str_stack = create_event_stack("hello") )pbdoc"); @@ -414,4 +414,4 @@ PYBIND11_MODULE(eventstack, m) { #else m.attr("PARALLEL_EXECUTION_SUPPORTED") = false; #endif -} \ No newline at end of file +} diff --git a/python/async/future.cpp b/python/async/future.cpp index c002a638..9054056e 100644 --- a/python/async/future.cpp +++ b/python/async/future.cpp @@ -19,25 +19,25 @@ void declare_enhanced_future(py::module& m, const std::string& type_name) { py::class_(m, class_name.c_str(), R"pbdoc( Enhanced future class with additional functionality beyond standard futures. - - This class extends std::future with features like chaining operations, + + This class extends std::future with features like chaining operations, callbacks, timeouts, cancellation, and more. - + Args: future: A shared_future to wrap (typically created by makeEnhancedFuture) - + Examples: >>> from atom.async.future import makeEnhancedFuture - >>> + >>> >>> # Create an enhanced future >>> future = makeEnhancedFuture(lambda: 42) - >>> + >>> >>> # Chain operations >>> result_future = future.then(lambda x: x * 2) - >>> + >>> >>> # Add completion callback >>> future.on_complete(lambda x: print(f"Result: {x}")) - >>> + >>> >>> # Get result with timeout >>> result = future.wait_for(5000) # 5 seconds timeout )pbdoc") @@ -48,10 +48,10 @@ void declare_enhanced_future(py::module& m, const std::string& type_name) { .def("wait", &EnhancedFutureT::wait, R"pbdoc( Waits synchronously for the future to complete. - + Returns: The value of the future. - + Raises: RuntimeError: If the future is cancelled or throws an exception. )pbdoc") @@ -64,10 +64,10 @@ void declare_enhanced_future(py::module& m, const std::string& type_name) { py::arg("timeout"), R"pbdoc( Waits for the future with a timeout and auto-cancels if not ready. - + Args: timeout: The timeout duration in milliseconds - + Returns: The value if ready, or None if timed out )pbdoc") @@ -77,10 +77,10 @@ void declare_enhanced_future(py::module& m, const std::string& type_name) { .def("get", &EnhancedFutureT::get, R"pbdoc( Gets the result of the future. - + Returns: The value of the future. - + Raises: RuntimeError: If the future is cancelled or throws an exception. )pbdoc") @@ -107,13 +107,13 @@ void declare_enhanced_future(py::module& m, const std::string& type_name) { py::arg("func"), R"pbdoc( Chains another operation to be called after the future is done. - + Args: func: The function to call when the future is done - + Returns: A new EnhancedFuture for the result of the function - + Examples: >>> future = makeEnhancedFuture(lambda: 10) >>> future2 = future.then(lambda x: x * 2) @@ -137,17 +137,17 @@ void declare_enhanced_future(py::module& m, const std::string& type_name) { py::arg("func"), R"pbdoc( Provides exception handling for the future. - + Args: func: The function to call when an exception occurs - + Returns: A new EnhancedFuture that will handle exceptions - + Examples: >>> def might_fail(): >>> raise ValueError("Something went wrong") - >>> + >>> >>> future = makeEnhancedFuture(might_fail) >>> safe_future = future.catching(lambda err: f"Error: {err}") >>> result = safe_future.get() # Will be "Error: Something went wrong" @@ -168,15 +168,15 @@ void declare_enhanced_future(py::module& m, const std::string& type_name) { py::arg("backoff_ms") = py::none(), R"pbdoc( Retries the operation associated with the future. - + Args: func: The function to call when retrying max_retries: The maximum number of retries backoff_ms: Optional backoff time between retries in milliseconds - + Returns: A new EnhancedFuture for the retry operation - + Examples: >>> future = makeEnhancedFuture(lambda: 10) >>> retry_future = future.retry(lambda x: x * 2, 3, 100) @@ -193,10 +193,10 @@ void declare_enhanced_future(py::module& m, const std::string& type_name) { py::arg("callback"), R"pbdoc( Sets a completion callback to be called when the future is done. - + Args: callback: The callback function to add - + Examples: >>> future = makeEnhancedFuture(lambda: 42) >>> future.on_complete(lambda x: print(f"Result: {x}")) @@ -211,22 +211,22 @@ void declare_enhanced_future_void(py::module& m) { py::class_(m, "EnhancedFutureVoid", R"pbdoc( Enhanced future class for void operations. - - This class extends std::future with features like chaining operations, + + This class extends std::future with features like chaining operations, callbacks, timeouts, cancellation, and more. - + Args: future: A shared_future to wrap (typically created by makeEnhancedFuture) - + Examples: >>> from atom.async.future import makeEnhancedFuture - >>> + >>> >>> # Create a void enhanced future >>> future = makeEnhancedFuture(lambda: None) - >>> + >>> >>> # Chain operations >>> result_future = future.then(lambda: "Operation completed") - >>> + >>> >>> # Add completion callback >>> future.on_complete(lambda: print("Done!")) )pbdoc") @@ -237,7 +237,7 @@ void declare_enhanced_future_void(py::module& m) { .def("wait", &EnhancedFutureVoid::wait, R"pbdoc( Waits synchronously for the future to complete. - + Raises: RuntimeError: If the future is cancelled or throws an exception. )pbdoc") @@ -247,10 +247,10 @@ void declare_enhanced_future_void(py::module& m) { py::arg("timeout"), R"pbdoc( Waits for the future with a timeout and auto-cancels if not ready. - + Args: timeout: The timeout duration in milliseconds - + Returns: True if completed successfully, False if timed out )pbdoc") @@ -259,7 +259,7 @@ void declare_enhanced_future_void(py::module& m) { .def("get", &EnhancedFutureVoid::get, R"pbdoc( Waits for the future to complete. - + Raises: RuntimeError: If the future is cancelled or throws an exception. )pbdoc") @@ -286,13 +286,13 @@ void declare_enhanced_future_void(py::module& m) { py::arg("func"), R"pbdoc( Chains another operation to be called after the future is done. - + Args: func: The function to call when the future is done - + Returns: A new EnhancedFuture for the result of the function - + Examples: >>> future = makeEnhancedFuture(lambda: None) >>> future2 = future.then(lambda: "Done!") @@ -310,10 +310,10 @@ void declare_enhanced_future_void(py::module& m) { py::arg("callback"), R"pbdoc( Sets a completion callback to be called when the future is done. - + Args: callback: The callback function to add - + Examples: >>> future = makeEnhancedFuture(lambda: None) >>> future.on_complete(lambda: print("Task completed!")) @@ -328,27 +328,27 @@ PYBIND11_MODULE(future, m) { This module provides enhanced future classes with additional functionality beyond standard futures, including chaining operations, callbacks, timeouts, cancellation support, and more. - + Key components: - EnhancedFuture: Extended future with additional functionality - makeEnhancedFuture: Factory function to create enhanced futures - whenAll: Synchronization for multiple futures - parallelProcess: Utility for parallel data processing - + Example: >>> from atom.async.future import makeEnhancedFuture, whenAll - >>> + >>> >>> # Create enhanced futures >>> future1 = makeEnhancedFuture(lambda: 10) >>> future2 = makeEnhancedFuture(lambda: 20) - >>> + >>> >>> # Chain operations >>> future3 = future1.then(lambda x: x * 2) - >>> + >>> >>> # Synchronize multiple futures >>> all_futures = whenAll(future1, future2, future3) >>> results = all_futures.get() # [10, 20, 20] - >>> + >>> >>> # With timeout and callbacks >>> future = makeEnhancedFuture(lambda: compute_something()) >>> future.on_complete(lambda x: print(f"Result: {x}")) @@ -398,13 +398,13 @@ PYBIND11_MODULE(future, m) { py::arg("func"), R"pbdoc( Creates an EnhancedFuture from a function. - + Args: func: The function to execute asynchronously - + Returns: An EnhancedFuture for the result of the function - + Examples: >>> future = makeEnhancedFuture(lambda: 42) >>> result = future.get() # 42 @@ -508,14 +508,14 @@ PYBIND11_MODULE(future, m) { py::arg("futures"), py::arg("timeout") = py::none(), R"pbdoc( Waits for all futures to complete and returns their results. - + Args: futures: List of futures to wait for timeout: Optional timeout in milliseconds - + Returns: List of results from all futures - + Examples: >>> future1 = makeEnhancedFuture(lambda: 10) >>> future2 = makeEnhancedFuture(lambda: 20) @@ -562,15 +562,15 @@ PYBIND11_MODULE(future, m) { py::arg("items"), py::arg("func"), py::arg("chunk_size") = 0, R"pbdoc( Processes items in parallel using multiple threads. - + Args: items: List of items to process func: Function to apply to each item chunk_size: Size of chunks to process together (0 = auto) - + Returns: List of futures containing the results - + Examples: >>> items = list(range(100)) >>> futures = parallelProcess(items, lambda x: x * x) @@ -604,14 +604,14 @@ PYBIND11_MODULE(future, m) { py::arg("future"), py::arg("timeout"), R"pbdoc( Gets the result of a future with a timeout. - + Args: future: The future to get the result from timeout: The timeout in seconds - + Returns: The result of the future - + Raises: InvalidFutureException: If the timeout is reached )pbdoc"); @@ -625,4 +625,4 @@ PYBIND11_MODULE(future, m) { // Add version information m.attr("__version__") = "1.0.0"; -} \ No newline at end of file +} diff --git a/python/async/generator.cpp b/python/async/generator.cpp index 76c8cf54..631b8775 100644 --- a/python/async/generator.cpp +++ b/python/async/generator.cpp @@ -20,10 +20,10 @@ yield values, similar to Python generators. Examples: >>> from atom.async import generator - >>> + >>> >>> # Create a generator from a range >>> g = generator.range_int(0, 5) - >>> + >>> >>> # Iterate through the generator >>> for value in g: >>> print(value) @@ -61,14 +61,14 @@ yields values and can also receive values from the caller. Examples: >>> from atom.async import generator - >>> + >>> >>> # Create a two-way generator >>> g = generator.create_echo_generator_int() - >>> + >>> >>> # Send values and get responses >>> value = g.next(42) # Send 42, get 42 back >>> print(value) - >>> + >>> >>> # Check if generator is done >>> print(g.done()) )") @@ -103,10 +103,10 @@ This generator yields values to the caller but doesn't receive input. Examples: >>> from atom.async import generator - >>> + >>> >>> # Create a one-way generator >>> g = generator.create_counter_generator_int(10) - >>> + >>> >>> # Get values >>> for i in range(5): >>> value = g.next() @@ -169,26 +169,26 @@ PYBIND11_MODULE(generator, m) { This module provides Python bindings for C++20 coroutine-based generators, allowing for efficient, lazy evaluation of sequences and bi-directional communication with coroutines. - + The module includes: - Standard generators that yield values in a sequence - Two-way generators that can both yield and receive values - Utility functions to create generators from ranges, sequences, etc. - Support for infinite generators with safe iteration - + Example: >>> from atom.async import generator - >>> + >>> >>> # Create a range generator >>> g = generator.range_int(0, 5) - >>> + >>> >>> # Iterate through all values >>> for i in g: >>> print(i) # Prints 0, 1, 2, 3, 4 - >>> + >>> >>> # Create an infinite generator (use with caution!) >>> inf_gen = generator.infinite_range_int(1, 2) - >>> + >>> >>> # Take only the first few values from infinite generator >>> for i, value in enumerate(inf_gen): >>> print(value) @@ -270,7 +270,7 @@ PYBIND11_MODULE(generator, m) { Examples: >>> g = range_int(0, 5) >>> list(g) # [0, 1, 2, 3, 4] - + >>> g = range_int(0, 10, 2) >>> list(g) # [0, 2, 4, 6, 8] )"); @@ -346,4 +346,4 @@ PYBIND11_MODULE(generator, m) { // Add version information m.attr("__version__") = "1.0.0"; -} \ No newline at end of file +} diff --git a/python/async/limiter.cpp b/python/async/limiter.cpp index 7da96bbf..b15f8a6b 100644 --- a/python/async/limiter.cpp +++ b/python/async/limiter.cpp @@ -30,25 +30,25 @@ PYBIND11_MODULE(limiter, m) { This module provides tools for controlling call rates, including rate limiting, debouncing, and throttling functions. - + The module includes: - RateLimiter for controlling call frequency with configurable limits - Debounce for delaying function execution after multiple calls - Throttle for limiting function execution to specific intervals - + Example: >>> from atom.async import limiter - >>> + >>> >>> # Create a rate limiter >>> rate_limiter = limiter.RateLimiter() - >>> + >>> >>> # Set limit for a specific function (5 calls per second) >>> rate_limiter.set_function_limit("my_api_call", 5, 1) - >>> + >>> >>> # Create a debounced function (waits 500ms after last call) >>> debounced_fn = limiter.create_debounce(lambda: print("Debounced!"), 500) >>> debounced_fn() # Will wait 500ms before executing - >>> + >>> >>> # Create a throttled function (executes at most once every 1000ms) >>> throttled_fn = limiter.create_throttle(lambda: print("Throttled!"), 1000) >>> throttled_fn() # Executes immediately @@ -101,7 +101,7 @@ This class manages rate limiting for different functions based on configurable s Examples: >>> limiter = RateLimiter() >>> limiter.set_function_limit("api_call", 10, 60) # 10 calls per minute - >>> + >>> >>> # In an async function: >>> async def call_api(): >>> await limiter.acquire("api_call") @@ -184,7 +184,7 @@ since the last time it was invoked. >>> debounced = create_debounce(lambda: print("Called!"), 500) >>> debounced() # Will wait 500ms before printing >>> debounced() # Resets the timer - >>> + >>> >>> # Leading execution (immediate first call) >>> debounced2 = create_debounce(lambda: print("Called!"), 500, leading=True) >>> debounced2() # Executes immediately, then waits for subsequent calls @@ -235,7 +235,7 @@ ignoring additional calls during that interval. >>> throttled = create_throttle(lambda: print("Called!"), 1000) >>> throttled() # Executes immediately >>> throttled() # Ignored until 1000ms have passed - >>> + >>> >>> # Force immediate execution on first call >>> throttled2 = create_throttle(lambda: print("Called!"), 1000, leading=True) >>> throttled2() # Executes immediately @@ -312,4 +312,4 @@ This method is intended to be used with Python's 'await' keyword. // 添加版本信息 m.attr("__version__") = "1.0.0"; -} \ No newline at end of file +} diff --git a/python/async/message_bus.cpp b/python/async/message_bus.cpp index c20bd924..73b8ddb4 100644 --- a/python/async/message_bus.cpp +++ b/python/async/message_bus.cpp @@ -108,10 +108,10 @@ void declare_message_type(py::module& m, const std::string& type_name) { Examples: >>> def handler(message): >>> print(f"Received: {message}") - >>> + >>> >>> # Subscribe to string messages >>> token = subscribe_string(bus, "notifications.system", handler) - >>> + >>> >>> # Unsubscribe later >>> unsubscribe_string(bus, token) )"); @@ -170,7 +170,7 @@ void declare_message_type(py::module& m, const std::string& type_name) { Examples: >>> # Publish a string message >>> publish_string(bus, "notifications.system", "System is shutting down") - >>> + >>> >>> # Publish with delay >>> publish_string(bus, "notifications.system", "Delayed message", 5000) )"); @@ -271,10 +271,10 @@ PYBIND11_MODULE(message_bus, m) { This module provides an event-driven communication system with hierarchical routing, filtering, and asynchronous message handling. - + The message bus allows components to communicate without direct coupling, enabling a modular, extensible architecture. - + Features: - Hierarchical message routing with namespace support - Type-safe message passing @@ -282,27 +282,27 @@ PYBIND11_MODULE(message_bus, m) { - Message filtering - Message history tracking - Delayed message publishing - + Example: >>> from atom.async.message_bus import PyIOContext, MessageBus >>> from atom.async.message_bus import publish_string, subscribe_string - >>> + >>> >>> # Create an IO context for async operations >>> io_context = PyIOContext() - >>> + >>> >>> # Create a message bus >>> bus = MessageBus.create_shared(io_context) - >>> + >>> >>> # Define a message handler >>> def message_handler(message): >>> print(f"Received: {message}") - >>> + >>> >>> # Subscribe to a message type >>> token = subscribe_string(bus, "system.notifications", message_handler) - >>> + >>> >>> # Publish a message >>> publish_string(bus, "system.notifications", "Hello from the message bus!") - >>> + >>> >>> # Publish with delay (5 seconds) >>> publish_string(bus, "system.notifications", "Delayed message", 5000) )pbdoc"; @@ -329,13 +329,13 @@ PYBIND11_MODULE(message_bus, m) { // Define the IO context wrapper py::class_(m, "PyIOContext", R"( Python-friendly wrapper for asio::io_context. - + This class manages a thread that processes asynchronous operations for the message bus. - + The IO context is automatically started on creation and stopped when the object is garbage collected. - + Examples: >>> io_context = PyIOContext() >>> bus = MessageBus.create_shared(io_context) @@ -346,10 +346,10 @@ PYBIND11_MODULE(message_bus, m) { py::class_>(m, "MessageBus", R"( A message bus for asynchronous event-driven communication. - + The MessageBus provides a way for components to communicate without direct coupling, using a publish-subscribe pattern with hierarchical routing. - + Examples: >>> io_context = PyIOContext() >>> bus = MessageBus.create_shared(io_context) @@ -393,4 +393,4 @@ PYBIND11_MODULE(message_bus, m) { m.attr("MAX_HISTORY_SIZE") = atom::async::MessageBus::K_MAX_HISTORY_SIZE; m.attr("MAX_SUBSCRIBERS_PER_MESSAGE") = atom::async::MessageBus::K_MAX_SUBSCRIBERS_PER_MESSAGE; -} \ No newline at end of file +} diff --git a/python/async/message_queue.cpp b/python/async/message_queue.cpp index 9b15def3..338e0112 100644 --- a/python/async/message_queue.cpp +++ b/python/async/message_queue.cpp @@ -96,23 +96,23 @@ and support for both synchronous and asynchronous message processing. Examples: >>> from atom.async.message_queue import PyIOContext, MessageQueueString - >>> + >>> >>> # Create an IO context for async operations >>> io_context = PyIOContext() - >>> + >>> >>> # Create a message queue >>> queue = MessageQueueString(io_context) - >>> + >>> >>> # Define a message handler >>> def message_handler(message): >>> print(f"Received: {message}") - >>> + >>> >>> # Subscribe to messages >>> queue.subscribe(message_handler, "my_subscriber") - >>> + >>> >>> # Start processing messages >>> queue.start_processing() - >>> + >>> >>> # Publish messages >>> queue.publish("Hello, world!") >>> queue.publish("Another message", 10) # Higher priority @@ -169,19 +169,19 @@ and support for both synchronous and asynchronous message processing. Examples: >>> # Basic subscription >>> queue.subscribe(lambda msg: print(msg), "basic_subscriber") - >>> + >>> >>> # Priority subscription - >>> queue.subscribe(lambda msg: print(f"High priority: {msg}"), + >>> queue.subscribe(lambda msg: print(f"High priority: {msg}"), >>> "high_priority", 10) - >>> + >>> >>> # With filter - >>> queue.subscribe(lambda msg: print(f"Filtered: {msg}"), - >>> "filtered", 0, + >>> queue.subscribe(lambda msg: print(f"Filtered: {msg}"), + >>> "filtered", 0, >>> lambda msg: "important" in msg) - >>> + >>> >>> # With timeout - >>> queue.subscribe(lambda msg: process_message(msg), - >>> "timeout_protected", 0, None, + >>> queue.subscribe(lambda msg: process_message(msg), + >>> "timeout_protected", 0, None, >>> timeout=5000) # 5 seconds )") @@ -206,10 +206,10 @@ and support for both synchronous and asynchronous message processing. >>> # Define a handler >>> def my_handler(msg): >>> print(msg) - >>> + >>> >>> # Subscribe >>> queue.subscribe(my_handler, "my_subscriber") - >>> + >>> >>> # Later, unsubscribe >>> queue.unsubscribe(my_handler) )") @@ -225,7 +225,7 @@ and support for both synchronous and asynchronous message processing. Examples: >>> # Publish a basic message >>> queue.publish("Hello, world!") - >>> + >>> >>> # Publish a high-priority message >>> queue.publish("Urgent message", 10) )") @@ -369,7 +369,7 @@ This method stops the background thread that processes messages. >>> # Wait for a message >>> msg = await queue.await_message() >>> print(f"Received: {msg}") - >>> + >>> >>> # Wait for a filtered message >>> important_msg = await queue.await_message( >>> lambda m: "important" in m) @@ -388,11 +388,11 @@ PYBIND11_MODULE(message_queue, m) { This module provides a priority-based message queue with filtering, timeouts, and support for both synchronous and asynchronous message processing. - + The message queue allows components to communicate through a publish-subscribe - pattern with priority handling, ensuring that high-priority messages are + pattern with priority handling, ensuring that high-priority messages are processed before lower-priority ones. - + Features: - Priority-based message processing - Message filtering @@ -400,30 +400,30 @@ PYBIND11_MODULE(message_queue, m) { - Asynchronous message processing - Python async/await support - Cancellation of pending messages - + Example: >>> from atom.async.message_queue import PyIOContext, MessageQueueString - >>> + >>> >>> # Create an IO context for async operations >>> io_context = PyIOContext() - >>> + >>> >>> # Create a message queue >>> queue = MessageQueueString(io_context) - >>> + >>> >>> # Define a message handler >>> def message_handler(message): >>> print(f"Received: {message}") - >>> + >>> >>> # Subscribe to messages >>> queue.subscribe(message_handler, "my_subscriber") - >>> + >>> >>> # Start processing messages >>> queue.start_processing() - >>> + >>> >>> # Publish messages >>> queue.publish("Hello, world!") >>> queue.publish("Another message", 10) # Higher priority - >>> + >>> >>> # Using async/await >>> async def process_messages(): >>> message = await queue.await_message() @@ -461,13 +461,13 @@ PYBIND11_MODULE(message_queue, m) { // Define the IO context wrapper py::class_(m, "PyIOContext", R"( Python-friendly wrapper for asio::io_context. - + This class manages a thread that processes asynchronous operations for the message queue. - + The IO context is automatically started on creation and stopped when the object is garbage collected. - + Examples: >>> io_context = PyIOContext() >>> queue = MessageQueueString(io_context) @@ -486,4 +486,4 @@ PYBIND11_MODULE(message_queue, m) { // Add version information m.attr("__version__") = "1.0.0"; -} \ No newline at end of file +} diff --git a/python/async/packaged_task.cpp b/python/async/packaged_task.cpp index 8ef89862..82bbacfe 100644 --- a/python/async/packaged_task.cpp +++ b/python/async/packaged_task.cpp @@ -53,7 +53,7 @@ PYBIND11_MODULE(packaged_task, m) { m, "PackagedTask", R"(Enhanced packaged task for executing deferred operations. -This class wraps a callable object and provides mechanisms to execute it +This class wraps a callable object and provides mechanisms to execute it asynchronously, returning its result through a future. Examples: @@ -124,7 +124,7 @@ asynchronously, returning its result through a future. m, "VoidPackagedTask", R"(Enhanced packaged task for executing deferred operations without return values. -This class wraps a callable object and provides mechanisms to execute it +This class wraps a callable object and provides mechanisms to execute it asynchronously, signaling completion through a future. Examples: @@ -277,7 +277,7 @@ asynchronously, signaling completion through a future. py::arg("task"), R"(Run a callable as a packaged task and return its future. -This is a convenience function that creates a packaged task, +This is a convenience function that creates a packaged task, executes it in a background thread, and returns a future. Args: @@ -292,4 +292,4 @@ executes it in a background thread, and returns a future. >>> result = future.result() >>> print(result) # Output: 42 )"); -} \ No newline at end of file +} diff --git a/python/async/parallel.cpp b/python/async/parallel.cpp index a2ea7442..8c9b2529 100644 --- a/python/async/parallel.cpp +++ b/python/async/parallel.cpp @@ -382,4 +382,4 @@ instructions for common vector operations like addition, multiplication and dot py::arg("b")) .def_static("dot_product", &simd_dot_product, py::arg("a"), py::arg("b")); -} \ No newline at end of file +} diff --git a/python/async/pool.cpp b/python/async/pool.cpp index 604a3f76..fac0e098 100644 --- a/python/async/pool.cpp +++ b/python/async/pool.cpp @@ -79,7 +79,7 @@ PYBIND11_MODULE(pool, m) { m, "ThreadSafeQueue", R"(A thread-safe queue implementation for storing Python objects. -This queue provides thread-safe operations for adding, removing, and +This queue provides thread-safe operations for adding, removing, and manipulating elements in a multi-threaded environment. Examples: @@ -347,4 +347,4 @@ Keyword Args: "hardware_concurrency", []() { return std::thread::hardware_concurrency(); }, "Returns the number of concurrent threads supported by the hardware."); -} \ No newline at end of file +} diff --git a/python/async/promise.cpp b/python/async/promise.cpp index 6fa66f00..ccead125 100644 --- a/python/async/promise.cpp +++ b/python/async/promise.cpp @@ -348,7 +348,7 @@ resolution and rejection mechanisms similar to JavaScript Promises. A new Promise that is resolved/rejected with the return value of the called handler. Examples: - >>> promise.then(lambda value: print(f"Success: {value}"), + >>> promise.then(lambda value: print(f"Success: {value}"), ... lambda reason: print(f"Failed: {reason}")) )") .def( @@ -519,4 +519,4 @@ resolution and rejection mechanisms similar to JavaScript Promises. >>> race_promise.wait() 'p2 done' )"); -} \ No newline at end of file +} diff --git a/python/async/queue.cpp b/python/async/queue.cpp index 147c4990..b93fa42a 100644 --- a/python/async/queue.cpp +++ b/python/async/queue.cpp @@ -373,4 +373,4 @@ A list of extracted elements. >>> queue.size() 3 )"); -} \ No newline at end of file +} diff --git a/python/async/safetype.cpp b/python/async/safetype.cpp index 1e7f6869..bfe851ae 100644 --- a/python/async/safetype.cpp +++ b/python/async/safetype.cpp @@ -587,4 +587,4 @@ simultaneously without explicit locking mechanisms. 3 >>> lst.front() # Should be item3 )"); -} \ No newline at end of file +} diff --git a/python/async/slot.cpp b/python/async/slot.cpp index af94869a..f7608226 100644 --- a/python/async/slot.cpp +++ b/python/async/slot.cpp @@ -130,7 +130,7 @@ This class provides a mechanism for implementing the observer pattern where func R"(A signal class that allows asynchronous slot execution. This class provides a mechanism for implementing the observer pattern where functions -(slots) can be connected to a signal and will be called asynchronously when the +(slots) can be connected to a signal and will be called asynchronously when the signal is emitted. Examples: @@ -254,7 +254,7 @@ uniquely identifiable connections that can be easily disconnected by ID. m, "ChainedSignal", R"(A signal class that allows chaining of signals. -This class provides a mechanism for implementing signal chains where emitting +This class provides a mechanism for implementing signal chains where emitting one signal will trigger others connected in a chain. Examples: @@ -687,4 +687,4 @@ automatic cleanup of slots when they are no longer referenced. >>> from atom.async import create_scoped_signal >>> signal = create_scoped_signal() )"); -} \ No newline at end of file +} diff --git a/python/async/thread_wrapper.cpp b/python/async/thread_wrapper.cpp index ed51b560..26aa6565 100644 --- a/python/async/thread_wrapper.cpp +++ b/python/async/thread_wrapper.cpp @@ -477,4 +477,4 @@ The callable can optionally accept a `StopToken` as its first argument. }, py::arg("func"), R"(Runs a function in the thread pool and returns a future for the result.)"); -} \ No newline at end of file +} diff --git a/python/async/threadlocal.cpp b/python/async/threadlocal.cpp index f093fbcd..fa13032e 100644 --- a/python/async/threadlocal.cpp +++ b/python/async/threadlocal.cpp @@ -410,4 +410,4 @@ Equivalent to calling .get(). }, "Support for boolean evaluation (True if current thread has a " "value)."); -} \ No newline at end of file +} diff --git a/python/async/timer.cpp b/python/async/timer.cpp index 38d24204..42d0c719 100644 --- a/python/async/timer.cpp +++ b/python/async/timer.cpp @@ -76,7 +76,7 @@ options for repetition and priority settings. m, "Timer", R"(Represents a timer for scheduling and executing tasks. -This class provides methods to schedule one-time or recurring tasks with +This class provides methods to schedule one-time or recurring tasks with precise timing control and priority settings. Examples: @@ -308,4 +308,4 @@ precise timing control and priority settings. ... print(f"Alert: {message}") >>> timer, future = schedule_timeout(alert, 2000, "Time's up!") )"); -} \ No newline at end of file +} diff --git a/python/async/trigger.cpp b/python/async/trigger.cpp index b883c5bd..c2976a39 100644 --- a/python/async/trigger.cpp +++ b/python/async/trigger.cpp @@ -97,7 +97,7 @@ for different events with support for priorities and delayed execution. TriggerException: If the event name is empty or the callback is invalid. Examples: - >>> callback_id = trigger.register_callback("data_received", + >>> callback_id = trigger.register_callback("data_received", ... lambda data: print(f"Got: {data}"), ... CallbackPriority.HIGH) )") @@ -282,4 +282,4 @@ for different events with support for priorities and delayed execution. >>> from atom.async import create_trigger >>> trigger = create_trigger() )"); -} \ No newline at end of file +} diff --git a/python/connection/__init__.py b/python/connection/__init__.py index 59191b7a..d1d39489 100644 --- a/python/connection/__init__.py +++ b/python/connection/__init__.py @@ -1 +1 @@ -# Auto-generated __init__.py for connection module \ No newline at end of file +# Auto-generated __init__.py for connection module diff --git a/python/connection/fifo.cpp b/python/connection/fifo.cpp index 748e3877..2b455087 100644 --- a/python/connection/fifo.cpp +++ b/python/connection/fifo.cpp @@ -116,4 +116,4 @@ This will release any resources associated with the FIFO. >>> from atom.connection.fifo import create_fifo_client >>> client = create_fifo_client("/tmp/my_fifo") )"); -} \ No newline at end of file +} diff --git a/python/connection/fifoserver.cpp b/python/connection/fifoserver.cpp index bc7d152c..476c4d89 100644 --- a/python/connection/fifoserver.cpp +++ b/python/connection/fifoserver.cpp @@ -97,4 +97,4 @@ This method stops the server, closes the FIFO, and joins any background threads. >>> server = create_fifo_server("/tmp/my_fifo") >>> server.start() )"); -} \ No newline at end of file +} diff --git a/python/connection/sockethub.cpp b/python/connection/sockethub.cpp index 949ffb23..55aabdcd 100644 --- a/python/connection/sockethub.cpp +++ b/python/connection/sockethub.cpp @@ -186,22 +186,22 @@ manage client groups, and process messages with customizable handlers. Examples: >>> from atom.connection.sockethub import SocketHub, Message, SocketHubConfig - >>> + >>> >>> # Create and configure the hub >>> config = SocketHubConfig() >>> config.connection_timeout = 60 >>> hub = SocketHub(config) - >>> + >>> >>> # Set up handlers >>> def on_message(message, client_id): ... print(f"Received: {message.as_string()} from client {client_id}") ... hub.broadcast_message(Message.create_text("Echo: " + message.as_string())) - >>> + >>> >>> hub.add_message_handler(on_message) - >>> + >>> >>> # Start the server >>> hub.start(8080) - >>> + >>> >>> # Keep the server running until manually stopped >>> try: ... # Your application logic here @@ -240,7 +240,7 @@ This method will disconnect all clients and release resources. Examples: >>> def message_handler(message, client_id): ... print(f"Message from {client_id}: {message.as_string()}") - ... + ... >>> hub.add_message_handler(message_handler) )") .def("add_connect_handler", @@ -254,7 +254,7 @@ This method will disconnect all clients and release resources. Examples: >>> def connect_handler(client_id, ip): ... print(f"Client {client_id} connected from {ip}") - ... + ... >>> hub.add_connect_handler(connect_handler) )") .def("add_disconnect_handler", @@ -268,7 +268,7 @@ This method will disconnect all clients and release resources. Examples: >>> def disconnect_handler(client_id, reason): ... print(f"Client {client_id} disconnected: {reason}") - ... + ... >>> hub.add_disconnect_handler(disconnect_handler) )") .def("add_error_handler", @@ -282,7 +282,7 @@ This method will disconnect all clients and release resources. Examples: >>> def error_handler(error, client_id): ... print(f"Error for client {client_id}: {error}") - ... + ... >>> hub.add_error_handler(error_handler) )") .def("broadcast_message", @@ -357,7 +357,7 @@ This method will disconnect all clients and release resources. >>> def authenticate(username, password): ... # Check credentials against a database, etc. ... return username == "admin" and password == "secret" - ... + ... >>> hub.set_authenticator(authenticate) )") .def("require_authentication", @@ -419,7 +419,7 @@ This method will disconnect all clients and release resources. >>> def log_handler(level, message): ... levels = ["DEBUG", "INFO", "WARNING", "ERROR", "FATAL"] ... print(f"[{levels[int(level)]}] {message}") - ... + ... >>> hub.set_log_handler(log_handler) )") .def("is_running", &atom::async::connection::SocketHub::isRunning, @@ -553,4 +553,4 @@ then starts it on the specified port. >>> msg = create_binary_message(bytearray([0x01, 0x02, 0x03])) >>> hub.broadcast_message(msg) )"); -} \ No newline at end of file +} diff --git a/python/connection/tcpclient.cpp b/python/connection/tcpclient.cpp index 97db2d9f..76e5ee6d 100644 --- a/python/connection/tcpclient.cpp +++ b/python/connection/tcpclient.cpp @@ -203,11 +203,11 @@ automatic reconnection, heartbeats, and configurable timeouts. >>> config = ConnectionConfig() >>> config.keep_alive = True >>> config.connect_timeout = 5000 # 5 seconds - >>> + >>> >>> client = TcpClient(config) >>> client.connect("example.com", 80) >>> client.send_string("GET / HTTP/1.1\r\nHost: example.com\r\n\r\n") - >>> + >>> >>> # Asynchronous receive >>> future = client.receive_until('\n', 1000) >>> response = future.result() @@ -487,7 +487,7 @@ This method zeroes all counters in the statistics object. Examples: >>> def on_connecting(): ... print("Connecting to server...") - ... + ... >>> client.set_on_connecting_callback(on_connecting) )") .def("set_on_connected_callback", @@ -501,7 +501,7 @@ This method zeroes all counters in the statistics object. Examples: >>> def on_connected(): ... print("Successfully connected to server") - ... + ... >>> client.set_on_connected_callback(on_connected) )") .def("set_on_disconnected_callback", @@ -515,7 +515,7 @@ This method zeroes all counters in the statistics object. Examples: >>> def on_disconnected(): ... print("Disconnected from server") - ... + ... >>> client.set_on_disconnected_callback(on_disconnected) )") .def("set_on_data_received_callback", @@ -529,7 +529,7 @@ This method zeroes all counters in the statistics object. Examples: >>> def on_data_received(data): ... print(f"Received {len(data)} bytes") - ... + ... >>> client.set_on_data_received_callback(on_data_received) )") .def("set_on_error_callback", @@ -543,7 +543,7 @@ This method zeroes all counters in the statistics object. Examples: >>> def on_error(error_msg): ... print(f"Error: {error_msg}") - ... + ... >>> client.set_on_error_callback(on_error) )") .def("set_on_state_changed_callback", @@ -557,7 +557,7 @@ This method zeroes all counters in the statistics object. Examples: >>> def on_state_changed(new_state, old_state): ... print(f"State changed from {old_state} to {new_state}") - ... + ... >>> client.set_on_state_changed_callback(on_state_changed) )") .def("set_on_heartbeat_callback", @@ -571,7 +571,7 @@ This method zeroes all counters in the statistics object. Examples: >>> def on_heartbeat(): ... print("Heartbeat sent") - ... + ... >>> client.set_on_heartbeat_callback(on_heartbeat) )") .def( @@ -666,4 +666,4 @@ This method zeroes all counters in the statistics object. ... except RuntimeError as e: ... print(f"Secure connection failed: {e}") )"); -} \ No newline at end of file +} diff --git a/python/connection/udp.cpp b/python/connection/udp.cpp index 62b8a172..65ad25c8 100644 --- a/python/connection/udp.cpp +++ b/python/connection/udp.cpp @@ -132,7 +132,7 @@ PYBIND11_MODULE(udp, m) { m, "UdpClient", R"(A modern UDP client for sending and receiving datagrams. -This class provides methods for UDP socket communication, including sending +This class provides methods for UDP socket communication, including sending and receiving datagrams, multicast support, broadcast support, and asynchronous operations. Examples: @@ -569,4 +569,4 @@ and receiving datagrams, multicast support, broadcast support, and asynchronous Returns: True if IPv6 is supported, False otherwise )"); -} \ No newline at end of file +} diff --git a/python/connection/udpserver.cpp b/python/connection/udpserver.cpp index e9aa2d52..e22ba8ee 100644 --- a/python/connection/udpserver.cpp +++ b/python/connection/udpserver.cpp @@ -92,12 +92,12 @@ asynchronous operations, multicast, broadcast, and more. Examples: >>> from atom.connection.udpserver import UdpSocketHub >>> server = UdpSocketHub() - >>> + >>> >>> # Set up message handler >>> def on_message(message, addr, port): ... print(f"Received from {addr}:{port}: {message}") ... return "Response: " + message - >>> + >>> >>> server.add_message_handler(on_message) >>> server.start(8080) # Start listening on port 8080 )") @@ -144,7 +144,7 @@ This method stops the server, closes the socket, and joins any worker threads. Examples: >>> def message_handler(message, ip, port): ... print(f"Received message from {ip}:{port}: {message}") - ... + ... >>> server.add_message_handler(message_handler) )") .def("remove_message_handler", @@ -167,7 +167,7 @@ This method stops the server, closes the socket, and joins any worker threads. Examples: >>> def error_handler(message, error_code): ... print(f"Error {error_code}: {message}") - ... + ... >>> server.add_error_handler(error_handler) )") .def("remove_error_handler", @@ -371,4 +371,4 @@ This function creates a UdpSocketHub, starts it, and joins a multicast group. >>> from atom.connection.udpserver import create_multicast_server >>> server = create_multicast_server(5000, "224.0.0.1") )"); -} \ No newline at end of file +} diff --git a/python/error/__init__.py b/python/error/__init__.py index ac8b725d..248ed704 100644 --- a/python/error/__init__.py +++ b/python/error/__init__.py @@ -1 +1 @@ -# Auto-generated __init__.py for error module \ No newline at end of file +# Auto-generated __init__.py for error module diff --git a/python/error/stacktrace.cpp b/python/error/stacktrace.cpp index 30038163..a83a416d 100644 --- a/python/error/stacktrace.cpp +++ b/python/error/stacktrace.cpp @@ -27,8 +27,8 @@ PYBIND11_MODULE(stacktrace, m) { m, "StackTrace", R"(Class for capturing and representing a stack trace with enhanced details. -This class captures the stack trace of the current execution context and represents -it as a string, including file names, line numbers, function names, module +This class captures the stack trace of the current execution context and represents +it as a string, including file names, line numbers, function names, module information, and memory addresses when available. Examples: @@ -129,7 +129,7 @@ enhanced error reporting. ... print(error_report) Exception: ValueError Message: Invalid input - + Native Stack Trace: [0] format_exception_with_traceback at ... [1] __main__ at ... @@ -192,4 +192,4 @@ the native stack trace and include it in the error message. ... # Some code that might raise a C++ exception ... pass )"); -} \ No newline at end of file +} diff --git a/python/extra/beast/http.cpp b/python/extra/beast/http.cpp index 2777fd41..d3c116f2 100644 --- a/python/extra/beast/http.cpp +++ b/python/extra/beast/http.cpp @@ -59,14 +59,14 @@ handling, file uploads and downloads, and more. Examples: >>> from atom.http import HttpClient, HttpVerb >>> import asyncio - >>> + >>> >>> # Synchronous request >>> client = HttpClient() >>> response = client.request(HttpVerb.GET, "example.com", "80", "/") >>> print(response.body()) - >>> + >>> >>> # JSON request - >>> json_response = client.json_request(HttpVerb.POST, "api.example.com", + >>> json_response = client.json_request(HttpVerb.POST, "api.example.com", >>> "443", "/data", {"key": "value"}) >>> print(json_response) )") @@ -443,4 +443,4 @@ headers, and body content. return headers; }, "Gets all headers as a dictionary."); -} \ No newline at end of file +} diff --git a/python/extra/beast/http_utils.cpp b/python/extra/beast/http_utils.cpp index 67125d60..f642bef8 100644 --- a/python/extra/beast/http_utils.cpp +++ b/python/extra/beast/http_utils.cpp @@ -131,7 +131,7 @@ Keys and values are URL-encoded. params: A dictionary where keys are parameter names and values are parameter values. Returns: - The formatted query string (e.g., "key1=value1&key2=value2"). + The formatted query string (e.g., "key1=value1&key2=value2"). Does not include the leading '?'. Examples: @@ -237,4 +237,4 @@ add_cookies_to_request for accurate cookie selection. The cookie value if found matching the host and name (considering domain matching), otherwise an empty string. Returns the first match found. )"); -} \ No newline at end of file +} diff --git a/python/extra/beast/ws.cpp b/python/extra/beast/ws.cpp index 039eab20..b3098bd2 100644 --- a/python/extra/beast/ws.cpp +++ b/python/extra/beast/ws.cpp @@ -41,16 +41,16 @@ messages, and manage connection settings like timeouts and reconnection. Examples: >>> from atom.ws import WSClient >>> import asyncio - >>> + >>> >>> # Create a WebSocket client >>> client = WSClient() - >>> + >>> >>> # Connect to a WebSocket server >>> client.connect("echo.websocket.org", "80") - >>> + >>> >>> # Send a message >>> client.send("Hello, WebSocket!") - >>> + >>> >>> # Receive a message >>> response = client.receive() >>> print(response) @@ -266,4 +266,4 @@ messages, and manage connection settings like timeouts and reconnection. Raises: RuntimeError: If not connected. )"); -} \ No newline at end of file +} diff --git a/python/extra/boost/charconv.cpp b/python/extra/boost/charconv.cpp index ec0d16d9..3aa8724e 100644 --- a/python/extra/boost/charconv.cpp +++ b/python/extra/boost/charconv.cpp @@ -304,4 +304,4 @@ with precise format control. m.def("string_to_float", &atom::extra::boost::BoostCharConv::stringToFloat, py::arg("str"), "Shorthand for BoostCharConv.string_to_float"); -} \ No newline at end of file +} diff --git a/python/extra/boost/locale.cpp b/python/extra/boost/locale.cpp index bc128d17..381028a8 100644 --- a/python/extra/boost/locale.cpp +++ b/python/extra/boost/locale.cpp @@ -368,4 +368,4 @@ number formatting, currency formatting, and regex replacement using Boost.Locale // Create a default instance m.attr("default_wrapper") = py::cast(atom::extra::boost::LocaleWrapper()); -} \ No newline at end of file +} diff --git a/python/extra/boost/math.cpp b/python/extra/boost/math.cpp index 4e662ca5..4db72c1c 100644 --- a/python/extra/boost/math.cpp +++ b/python/extra/boost/math.cpp @@ -19,7 +19,7 @@ void declare_math_classes(py::module& m, const std::string& type_suffix) { m, class_name.c_str(), R"(Wrapper class for special mathematical functions. -This class provides various special mathematical functions like beta, gamma, +This class provides various special mathematical functions like beta, gamma, digamma, error function, Bessel functions, and Legendre polynomials. Examples: @@ -496,7 +496,7 @@ This class provides optimization methods like golden section search and Newton-R py::class_(m, class_name.c_str(), R"(Wrapper class for linear algebra operations. -This class provides linear algebra operations such as solving linear systems, +This class provides linear algebra operations such as solving linear systems, computing determinants, matrix multiplication, and transpose. Examples: @@ -601,7 +601,7 @@ This class provides methods for solving ODEs such as the 4th order Runge-Kutta m m, class_name.c_str(), R"(Wrapper class for financial mathematics functions. -This class provides financial math functions such as Black-Scholes option pricing, +This class provides financial math functions such as Black-Scholes option pricing, bond pricing, and implied volatility calculation. Examples: @@ -739,4 +739,4 @@ PYBIND11_MODULE(math, m) { // Add version info m.attr("__version__") = "1.0.0"; -} \ No newline at end of file +} diff --git a/python/extra/boost/regex.cpp b/python/extra/boost/regex.cpp index dfc9c0d7..1f6b98d6 100644 --- a/python/extra/boost/regex.cpp +++ b/python/extra/boost/regex.cpp @@ -539,4 +539,4 @@ using the Boost.Regex library. // Add version info m.attr("__version__") = "1.0.0"; -} \ No newline at end of file +} diff --git a/python/extra/boost/system.cpp b/python/extra/boost/system.cpp index e9070c94..e0bbea3e 100644 --- a/python/extra/boost/system.cpp +++ b/python/extra/boost/system.cpp @@ -153,7 +153,7 @@ If the function throws an exception, it's caught and converted to an Error. >>> result = system.make_result(success_func) >>> print(result.value()) Success! - + >>> def error_func(): ... raise ValueError("Something went wrong") >>> result = system.make_result(error_func) @@ -208,7 +208,7 @@ This specialization is used for functions that don't return a value but might fa >>> result = system.ResultVoid() >>> print(result.has_value()) True - + >>> # Creating a failed void result >>> error_result = system.ResultVoid(system.Error(1, system.generic_category())) >>> print(error_result.has_value()) @@ -239,7 +239,7 @@ This class either contains a value of the specified type or an error. >>> result = system.ResultInt(42) >>> print(result.value()) 42 - + >>> # Creating a failed result >>> error_result = system.ResultInt(system.Error(1, system.generic_category())) >>> print(error_result.has_value()) @@ -333,4 +333,4 @@ This class either contains a value of the specified type or an error. }); */ } -} \ No newline at end of file +} diff --git a/python/extra/boost/uuid.cpp b/python/extra/boost/uuid.cpp index 9519110c..a302f991 100644 --- a/python/extra/boost/uuid.cpp +++ b/python/extra/boost/uuid.cpp @@ -42,7 +42,7 @@ in various formats. >>> id1 = uuid.UUID() >>> print(id1.to_string()) 550e8400-e29b-41d4-a716-446655440000 - + >>> # Create UUID from string >>> id2 = uuid.UUID("550e8400-e29b-41d4-a716-446655440000") >>> print(id2.format()) @@ -230,4 +230,4 @@ in various formats. // Add version info m.attr("__version__") = "1.0.0"; -} \ No newline at end of file +} diff --git a/python/io/__init__.py b/python/io/__init__.py index 0e05c922..27570b4c 100644 --- a/python/io/__init__.py +++ b/python/io/__init__.py @@ -1 +1 @@ -# Auto-generated __init__.py for io module \ No newline at end of file +# Auto-generated __init__.py for io module diff --git a/python/io/asyncio.cpp b/python/io/asyncio.cpp index 0e521a82..da11c270 100644 --- a/python/io/asyncio.cpp +++ b/python/io/asyncio.cpp @@ -246,16 +246,16 @@ This class provides methods for reading, writing, and manipulating files asynchr Examples: >>> import asio >>> from atom.io.asyncio import AsyncFile - >>> + >>> >>> io_context = asio.io_context() >>> async_file = AsyncFile(io_context) - >>> + >>> >>> def on_read(result): ... if result.success: ... print(f"Read {len(result.value)} bytes") ... else: ... print(f"Error: {result.error_message}") - >>> + >>> >>> async_file.async_read("example.txt", on_read) >>> io_context.run() )") @@ -286,7 +286,7 @@ This class provides methods for reading, writing, and manipulating files asynchr ... print(f"Content: {result.value[:50]}...") ... else: ... print(f"Error: {result.error_message}") - >>> + >>> >>> async_file.async_read("example.txt", on_read) )") .def( @@ -316,7 +316,7 @@ This class provides methods for reading, writing, and manipulating files asynchr ... print("Write successful") ... else: ... print(f"Error: {result.error_message}") - >>> + >>> >>> async_file.async_write("example.txt", "Hello, World!", on_write) )") .def( @@ -343,7 +343,7 @@ This class provides methods for reading, writing, and manipulating files asynchr ... print("Delete successful") ... else: ... print(f"Error: {result.error_message}") - >>> + >>> >>> async_file.async_delete("temporary.txt", on_delete) )") .def( @@ -371,7 +371,7 @@ This class provides methods for reading, writing, and manipulating files asynchr ... print("Copy successful") ... else: ... print(f"Error: {result.error_message}") - >>> + >>> >>> async_file.async_copy("original.txt", "backup.txt", on_copy) )") .def( @@ -400,7 +400,7 @@ This class provides methods for reading, writing, and manipulating files asynchr ... print(f"Read successful: {len(result.value }) bytes") ... else: ... print(f"Error: {result.error_message}") - >>> + >>> >>> async_file.async_read_with_timeout("example.txt", 1000, on_read) # 1 second timeout )") .def( @@ -431,7 +431,7 @@ This class provides methods for reading, writing, and manipulating files asynchr ... print(f"File {i+1}: {len(content)} bytes") ... else: ... print(f"Error: {result.error_message}") - >>> + >>> >>> async_file.async_batch_read(["file1.txt", "file2.txt"], on_batch_read) )") .def( @@ -462,7 +462,7 @@ This class provides methods for reading, writing, and manipulating files asynchr ... print("It's a regular file") ... else: ... print(f"Error: {result.error_message}") - >>> + >>> >>> async_file.async_stat("example.txt", on_stat) )") .def( @@ -490,7 +490,7 @@ This class provides methods for reading, writing, and manipulating files asynchr ... print("Move successful") ... else: ... print(f"Error: {result.error_message}") - >>> + >>> >>> async_file.async_move("old_path.txt", "new_path.txt", on_move) )") .def( @@ -516,13 +516,13 @@ This class provides methods for reading, writing, and manipulating files asynchr >>> import stat >>> from pathlib import Path >>> perms = stat.S_IRUSR | stat.S_IWUSR # Read & write for owner only - >>> + >>> >>> def on_chmod(result): ... if result.success: ... print("Changed permissions successfully") ... else: ... print(f"Error: {result.error_message}") - >>> + >>> >>> async_file.async_change_permissions("example.txt", perms, on_chmod) )") .def( @@ -549,7 +549,7 @@ This class provides methods for reading, writing, and manipulating files asynchr ... print("Directory created successfully") ... else: ... print(f"Error: {result.error_message}") - >>> + >>> >>> async_file.async_create_directory("new_directory", on_create_dir) )") .def( @@ -579,7 +579,7 @@ This class provides methods for reading, writing, and manipulating files asynchr ... print("File does not exist") ... else: ... print(f"Error: {result.error_message}") - >>> + >>> >>> async_file.async_exists("example.txt", on_exists) )") .def( @@ -679,10 +679,10 @@ This class provides methods for creating, removing, and listing directories asyn Examples: >>> import asio >>> from atom.io.asyncio import AsyncDirectory - >>> + >>> >>> io_context = asio.io_context() >>> async_dir = AsyncDirectory(io_context) - >>> + >>> >>> def on_list(result): ... if result.success: ... print(f"Found {len(result.value)} entries:") @@ -690,7 +690,7 @@ This class provides methods for creating, removing, and listing directories asyn ... print(f" - {path}") ... else: ... print(f"Error: {result.error_message}") - >>> + >>> >>> async_dir.async_list_contents(".", on_list) >>> io_context.run() )") @@ -721,7 +721,7 @@ This class provides methods for creating, removing, and listing directories asyn ... print("Directory created successfully") ... else: ... print(f"Error: {result.error_message}") - >>> + >>> >>> async_dir.async_create("new_directory", on_create) )") .def( @@ -748,7 +748,7 @@ This class provides methods for creating, removing, and listing directories asyn ... print("Directory removed successfully") ... else: ... print(f"Error: {result.error_message}") - >>> + >>> >>> async_dir.async_remove("old_directory", on_remove) )") .def( @@ -778,7 +778,7 @@ This class provides methods for creating, removing, and listing directories asyn ... print(f" - {path}") ... else: ... print(f"Error: {result.error_message}") - >>> + >>> >>> async_dir.async_list_contents(".", on_list) )") .def( @@ -808,7 +808,7 @@ This class provides methods for creating, removing, and listing directories asyn ... print("Directory does not exist") ... else: ... print(f"Error: {result.error_message}") - >>> + >>> >>> async_dir.async_exists("my_directory", on_exists) )") .def( @@ -960,4 +960,4 @@ This class provides methods for creating, removing, and listing directories asyn ... else: ... print(f"Error: {result.error_message}") )"); -} \ No newline at end of file +} diff --git a/python/io/compress.cpp b/python/io/compress.cpp index c002a384..02fab6e0 100644 --- a/python/io/compress.cpp +++ b/python/io/compress.cpp @@ -514,4 +514,4 @@ This class calculates the total size of a ZIP archive. >>> io_context.run() >>> print(f"File removed successfully: {success}") )"); -} \ No newline at end of file +} diff --git a/python/io/dirstack.cpp b/python/io/dirstack.cpp index 84913509..57264807 100644 --- a/python/io/dirstack.cpp +++ b/python/io/dirstack.cpp @@ -146,17 +146,17 @@ allowing you to maintain a directory stack for easy navigation. Examples: >>> import asio >>> from atom.io.dirstack import DirectoryStack - >>> + >>> >>> io_context = asio.io_context() >>> dirstack = DirectoryStack(io_context) - >>> + >>> >>> # Push current directory and change to a new one >>> def on_push(error): ... if not error: ... print("Successfully changed directory") ... else: ... print(f"Error: {error.message()}") - >>> + >>> >>> dirstack.async_pushd("/tmp", on_push) >>> io_context.run() )") @@ -185,7 +185,7 @@ allowing you to maintain a directory stack for easy navigation. ... print("Successfully changed directory") ... else: ... print(f"Error: {error.message()}") - >>> + >>> >>> dirstack.async_pushd("/tmp", on_push) )") .def( @@ -203,11 +203,11 @@ This method returns a coroutine-compatible Task object. Examples: >>> import asyncio - >>> + >>> >>> async def change_dir(): ... await dirstack.pushd("/tmp").__await__() ... print("Directory changed") - >>> + >>> >>> asyncio.run(change_dir()) )") .def( @@ -230,7 +230,7 @@ This method returns a coroutine-compatible Task object. ... print("Successfully changed back to previous directory") ... else: ... print(f"Error: {error.message()}") - >>> + >>> >>> dirstack.async_popd(on_pop) )") .def("popd", &atom::io::DirectoryStack::popd, @@ -243,11 +243,11 @@ This method returns a coroutine-compatible Task object. Examples: >>> import asyncio - >>> + >>> >>> async def pop_dir(): ... await dirstack.popd().__await__() ... print("Returned to previous directory") - >>> + >>> >>> asyncio.run(pop_dir()) )") .def("peek", &atom::io::DirectoryStack::peek, @@ -322,7 +322,7 @@ This method returns a coroutine-compatible Task object. ... print("Changed to directory at index") ... else: ... print(f"Error: {error.message()}") - >>> + >>> >>> dirstack.async_goto_index(2, on_goto) # Change to the directory at index 2 )") .def("goto_index", &atom::io::DirectoryStack::gotoIndex, @@ -339,11 +339,11 @@ This method returns a coroutine-compatible Task object. Examples: >>> import asyncio - >>> + >>> >>> async def goto_dir(): ... await dirstack.goto_index(2).__await__() ... print("Changed to directory at index 2") - >>> + >>> >>> asyncio.run(goto_dir()) )") .def( @@ -369,7 +369,7 @@ This method returns a coroutine-compatible Task object. ... print("Stack saved to file") ... else: ... print(f"Error saving stack: {error.message()}") - >>> + >>> >>> dirstack.async_save_stack_to_file("dirstack.txt", on_save) )") .def("save_stack_to_file", &atom::io::DirectoryStack::saveStackToFile, @@ -386,11 +386,11 @@ This method returns a coroutine-compatible Task object. Examples: >>> import asyncio - >>> + >>> >>> async def save_stack(): ... await dirstack.save_stack_to_file("dirstack.txt").__await__() ... print("Stack saved to file") - >>> + >>> >>> asyncio.run(save_stack()) )") .def( @@ -416,7 +416,7 @@ This method returns a coroutine-compatible Task object. ... print("Stack loaded from file") ... else: ... print(f"Error loading stack: {error.message()}") - >>> + >>> >>> dirstack.async_load_stack_from_file("dirstack.txt", on_load) )") .def("load_stack_from_file", @@ -433,11 +433,11 @@ This method returns a coroutine-compatible Task object. Examples: >>> import asyncio - >>> + >>> >>> async def load_stack(): ... await dirstack.load_stack_from_file("dirstack.txt").__await__() ... print("Stack loaded from file") - >>> + >>> >>> asyncio.run(load_stack()) )") .def("size", &atom::io::DirectoryStack::size, @@ -485,7 +485,7 @@ This method returns a coroutine-compatible Task object. Examples: >>> def on_get_dir(path): ... print(f"Current directory: {path}") - >>> + >>> >>> dirstack.async_get_current_directory(on_get_dir) )") .def("get_current_directory", @@ -499,11 +499,11 @@ This method returns a coroutine-compatible Task object. Examples: >>> import asyncio - >>> + >>> >>> async def print_current_dir(): ... path = await dirstack.get_current_directory().__await__() ... print(f"Current directory: {path}") - >>> + >>> >>> asyncio.run(print_current_dir()) )") .def("__len__", &atom::io::DirectoryStack::size, @@ -533,7 +533,7 @@ This method returns a coroutine-compatible Task object. Examples: >>> import asio >>> from atom.io.dirstack import create_directory_stack - >>> + >>> >>> io_context = asio.io_context() >>> dirstack = create_directory_stack(io_context) )"); @@ -579,4 +579,4 @@ This method returns a coroutine-compatible Task object. ... else: ... print("Failed to change directory") )"); -} \ No newline at end of file +} diff --git a/python/io/glob.cpp b/python/io/glob.cpp index 1771c6b8..e91ce9ba 100644 --- a/python/io/glob.cpp +++ b/python/io/glob.cpp @@ -46,19 +46,19 @@ supporting both synchronous and asynchronous operations. Examples: >>> import asio >>> from atom.io.glob import AsyncGlob - >>> + >>> >>> # Create an io_context and glob object >>> io_context = asio.io_context() >>> glob = AsyncGlob(io_context) - >>> + >>> >>> # Example of synchronous usage >>> matches = glob.glob_sync("*.txt") >>> print(f"Found {len(matches)} text files") - >>> + >>> >>> # Example of asynchronous usage with callback >>> def on_files_found(files): ... print(f"Found {len(files)} files") - >>> + >>> >>> glob.glob("*.py", on_files_found, recursive=True) >>> io_context.run() )") @@ -92,7 +92,7 @@ supporting both synchronous and asynchronous operations. ... print(f"Matched {len(files)} files") ... for file in files: ... print(f" - {file}") - >>> + >>> >>> glob.glob("*.py", print_matches) >>> io_context.run() # Run the ASIO io_context )") @@ -259,4 +259,4 @@ This is a convenience function that works like Python's glob.glob() with recursi >>> escape("file[1].txt") # Escapes the brackets 'file\\[1\\].txt' )"); -} \ No newline at end of file +} diff --git a/python/pybind11_json.hpp b/python/pybind11_json.hpp index 71ff1c60..9ff0242c 100644 --- a/python/pybind11_json.hpp +++ b/python/pybind11_json.hpp @@ -206,4 +206,4 @@ struct type_caster { } // namespace detail } // namespace pybind11 -#endif \ No newline at end of file +#endif diff --git a/python/search/__init__.py b/python/search/__init__.py index 9d600c45..1c0d8148 100644 --- a/python/search/__init__.py +++ b/python/search/__init__.py @@ -1 +1 @@ -# Auto-generated __init__.py for search module \ No newline at end of file +# Auto-generated __init__.py for search module diff --git a/python/search/cache.cpp b/python/search/cache.cpp index 23e97b86..25299432 100644 --- a/python/search/cache.cpp +++ b/python/search/cache.cpp @@ -355,4 +355,4 @@ This class provides methods to insert, retrieve, and manage cached string resour Returns: A FloatCache object. )"); -} \ No newline at end of file +} diff --git a/python/search/lru.cpp b/python/search/lru.cpp index 8558ea9f..f8d18605 100644 --- a/python/search/lru.cpp +++ b/python/search/lru.cpp @@ -633,4 +633,4 @@ Thread-safe LRU cache implementation optimized for floating-point values. Returns: A new FloatCache instance )"); -} \ No newline at end of file +} diff --git a/python/search/mysql.cpp b/python/search/mysql.cpp index 43408482..785c27c4 100644 --- a/python/search/mysql.cpp +++ b/python/search/mysql.cpp @@ -463,4 +463,4 @@ Provides connection management and various query execution methods. Returns: ResultSet object with paginated results )"); -} \ No newline at end of file +} diff --git a/python/search/search.cpp b/python/search/search.cpp index f50014ec..711e3efd 100644 --- a/python/search/search.cpp +++ b/python/search/search.cpp @@ -211,4 +211,4 @@ Supports operators AND, OR, NOT, and parentheses. Raises: IOError: If the file cannot be read )"); -} \ No newline at end of file +} diff --git a/python/search/sqlite.cpp b/python/search/sqlite.cpp index ff6d2f88..f1565a19 100644 --- a/python/search/sqlite.cpp +++ b/python/search/sqlite.cpp @@ -255,4 +255,4 @@ from SQLite databases. Returns: The number of rows modified )"); -} \ No newline at end of file +} diff --git a/python/search/ttl.cpp b/python/search/ttl.cpp index 56433b0a..817ef28f 100644 --- a/python/search/ttl.cpp +++ b/python/search/ttl.cpp @@ -91,7 +91,7 @@ PYBIND11_MODULE(ttl, m) { m, "StringCache", R"(A Time-to-Live (TTL) Cache with string keys and string values. -This class implements a TTL cache with an LRU eviction policy. Items in the cache +This class implements a TTL cache with an LRU eviction policy. Items in the cache expire after a specified duration and are evicted when the cache exceeds its maximum capacity. Args: @@ -201,4 +201,4 @@ This cache implements an LRU eviction policy with automatic expiration of items. Returns: A new FloatCache instance )"); -} \ No newline at end of file +} diff --git a/python/sysinfo/__init__.py b/python/sysinfo/__init__.py index 51240b02..dfd68b92 100644 --- a/python/sysinfo/__init__.py +++ b/python/sysinfo/__init__.py @@ -1 +1 @@ -# Auto-generated __init__.py for sysinfo module \ No newline at end of file +# Auto-generated __init__.py for sysinfo module diff --git a/python/sysinfo/battery.cpp b/python/sysinfo/battery.cpp index 5571e330..41d254f5 100644 --- a/python/sysinfo/battery.cpp +++ b/python/sysinfo/battery.cpp @@ -76,14 +76,14 @@ voltage, current, and other properties. "Battery serial number") .def("get_battery_health", &BatteryInfo::getBatteryHealth, R"(Calculate battery health (0-100%). - + Returns: Battery health percentage. )") .def("get_estimated_time_remaining", &BatteryInfo::getEstimatedTimeRemaining, R"(Estimate remaining usage time. - + Returns: Estimated time remaining in hours. )") @@ -235,18 +235,18 @@ This class provides static methods to start and stop battery monitoring. Examples: >>> from atom.sysinfo import battery >>> import time - >>> + >>> >>> # Define callback function for battery updates >>> def on_battery_update(info): ... print(f"Battery level: {info.battery_life_percent}%") ... print(f"Charging: {info.is_charging}") - ... + ... >>> # Start monitoring with 2 second interval >>> battery.BatteryMonitor.start_monitoring(on_battery_update, 2000) - >>> + >>> >>> # Let it run for a while >>> time.sleep(10) - >>> + >>> >>> # Stop monitoring >>> battery.BatteryMonitor.stop_monitoring() )") @@ -276,7 +276,7 @@ This class provides static methods to start and stop battery monitoring. >>> # Define a callback function >>> def on_battery_update(info): ... print(f"Battery update - Level: {info.battery_life_percent}%") - ... + ... >>> # Start monitoring with 1 second intervals >>> battery.BatteryMonitor.start_monitoring(on_battery_update) )") @@ -301,14 +301,14 @@ and alert functionality. >>> from atom.sysinfo import battery >>> # Get the singleton instance >>> manager = battery.BatteryManager.get_instance() - >>> + >>> >>> # Set up alert callback >>> def on_battery_alert(alert_msg, info): ... print(f"Battery alert: {alert_msg}") ... print(f"Current level: {info.battery_life_percent}%") - ... + ... >>> manager.set_alert_callback(on_battery_alert) - >>> + >>> >>> # Start monitoring >>> manager.start_monitoring(5000) # Check every 5 seconds )") @@ -364,17 +364,17 @@ and alert functionality. Args: callback: Function to call when a battery alert is triggered. - The callback receives two arguments: alert message (str) + The callback receives two arguments: alert message (str) and battery info (BatteryInfo). Examples: >>> from atom.sysinfo import battery >>> mgr = battery.BatteryManager.get_instance() - >>> + >>> >>> def alert_handler(alert_msg, info): ... print(f"Alert: {alert_msg}") ... print(f"Battery level: {info.battery_life_percent}%") - ... + ... >>> mgr.set_alert_callback(alert_handler) )") .def("set_alert_settings", &BatteryManager::setAlertSettings, @@ -390,7 +390,7 @@ and alert functionality. >>> settings = battery.BatteryAlertSettings() >>> settings.low_battery_threshold = 25.0 >>> settings.high_temp_threshold = 42.0 - >>> + >>> >>> # Apply settings >>> mgr = battery.BatteryManager.get_instance() >>> mgr.set_alert_settings(settings) @@ -465,11 +465,11 @@ and alert functionality. Examples: >>> from atom.sysinfo import battery >>> import datetime - >>> + >>> >>> mgr = battery.BatteryManager.get_instance() >>> # Get the last 10 history entries >>> history = mgr.get_history(10) - >>> + >>> >>> for timestamp, info in history: ... # Convert timestamp to readable format ... time_str = datetime.datetime.fromtimestamp( @@ -490,7 +490,7 @@ This class provides static methods to get and set the current power plan. >>> # Get current power plan >>> current_plan = battery.PowerPlanManager.get_current_power_plan() >>> print(f"Current power plan: {current_plan}") - >>> + >>> >>> # Switch to power saver >>> success = battery.PowerPlanManager.set_power_plan(battery.PowerPlan.POWER_SAVER) >>> if success: @@ -724,15 +724,15 @@ provided callback and cleans up when the context is exited. Examples: >>> from atom.sysinfo import battery >>> import time - >>> + >>> >>> def process_battery_info(info): ... print(f"Battery level: {info.battery_life_percent}%") - ... + ... >>> # Use as a context manager >>> with battery.monitor_battery(process_battery_info, 2000): ... print("Monitoring battery for 10 seconds...") ... time.sleep(10) - ... + ... >>> print("Monitoring stopped") )"); @@ -815,4 +815,4 @@ provided callback and cleans up when the context is exited. >>> time_str = battery.format_time_remaining() >>> print(f"Time remaining: {time_str}") )"); -} \ No newline at end of file +} diff --git a/python/sysinfo/bios.cpp b/python/sysinfo/bios.cpp index 829a2e29..20883840 100644 --- a/python/sysinfo/bios.cpp +++ b/python/sysinfo/bios.cpp @@ -145,7 +145,7 @@ look for updates, and perform BIOS-related operations. >>> from atom.sysinfo import bios >>> # Get the singleton instance >>> bios_mgr = bios.BiosInfo.get_instance() - >>> + >>> >>> # Get basic BIOS information >>> info = bios_mgr.get_bios_info() >>> print(f"BIOS version: {info.version}") @@ -182,7 +182,7 @@ look for updates, and perform BIOS-related operations. >>> # Get cached BIOS info >>> info = bios.BiosInfo.get_instance().get_bios_info() >>> print(f"BIOS version: {info.version}") - >>> + >>> >>> # Force update and get fresh info >>> info = bios.BiosInfo.get_instance().get_bios_info(True) )", @@ -518,13 +518,13 @@ look for updates, and perform BIOS-related operations. >>> print(f"BIOS version: {summary['version']}") >>> print(f"Manufacturer: {summary['manufacturer']}") >>> print(f"Age: {summary['age_in_days']} days") - >>> + >>> >>> if summary['update_available']: ... print(f"Update available: {summary['latest_version']}") - >>> + >>> >>> if summary['warnings']: ... print("Warnings:") ... for warning in summary['warnings']: ... print(f"- {warning}") )"); -} \ No newline at end of file +} diff --git a/python/sysinfo/cpu.cpp b/python/sysinfo/cpu.cpp index 0bd29c4e..3dd01a4e 100644 --- a/python/sysinfo/cpu.cpp +++ b/python/sysinfo/cpu.cpp @@ -105,7 +105,7 @@ its frequency, temperature, and utilization. py::class_(m, "CacheSizes", R"(CPU cache size information. -This class provides information about the sizes and characteristics of the +This class provides information about the sizes and characteristics of the various CPU caches. Examples: @@ -804,7 +804,7 @@ usage, temperature, and frequency and calls the provided callback with this data Args: interval_sec: How often to check CPU status, in seconds (default: 1.0). callback: Function to call with CPU data. The callback receives six arguments: - usage (float), temperature (float), frequency (float), + usage (float), temperature (float), frequency (float), core_usage (list), core_temperatures (list), core_frequencies (list). Returns: @@ -813,16 +813,16 @@ usage, temperature, and frequency and calls the provided callback with this data Examples: >>> from atom.sysinfo import cpu >>> import time - >>> + >>> >>> # Define a callback function >>> def cpu_callback(usage, temp, freq, core_usage, core_temps, core_freqs): ... print(f"CPU Usage: {usage:.1f}%, Temp: {temp:.1f}°C, Freq: {freq:.2f} GHz") - ... + ... >>> # Use as a context manager >>> with cpu.monitor_cpu(0.5, cpu_callback): ... print("Monitoring CPU for 5 seconds...") ... time.sleep(5) - ... + ... >>> print("Monitoring stopped") )"); @@ -963,4 +963,4 @@ usage, temperature, and frequency and calls the provided callback with this data >>> print(f"AVX support: {avx_support}") >>> print(f"AVX2 support: {avx2_support}") )"); -} \ No newline at end of file +} diff --git a/python/sysinfo/disk.cpp b/python/sysinfo/disk.cpp index e716b2d9..396b34b9 100644 --- a/python/sysinfo/disk.cpp +++ b/python/sysinfo/disk.cpp @@ -145,7 +145,7 @@ and device model information. >>> all_disks = disk.get_disk_info() >>> for d in all_disks: ... print(f"{d.path}: {d.usage_percent:.1f}% used") - >>> + >>> >>> # Get only fixed disks (exclude removable) >>> fixed_disks = disk.get_disk_info(include_removable=False) )"); @@ -201,7 +201,7 @@ and usage. For more detailed information, use get_disk_info() instead. >>> devices = disk.get_storage_devices() >>> for device in devices: ... print(f"{device.model} ({device.size_bytes / (1024**3):.1f} GB) - >>> + >>> >>> # Get only fixed storage devices (exclude removable) >>> fixed_devices = disk.get_storage_devices(include_removable=False) )"); @@ -235,7 +235,7 @@ and usage. For more detailed information, use get_disk_info() instead. >>> # Get all available drives >>> drives = disk.get_available_drives() >>> print(f"Available drives: {', '.join(drives)}") - >>> + >>> >>> # Get only fixed drives >>> fixed_drives = disk.get_available_drives(include_removable=False) )"); @@ -395,19 +395,19 @@ and usage. For more detailed information, use get_disk_info() instead. Examples: >>> from atom.sysinfo import disk >>> import time - >>> + >>> >>> # Define callback function >>> def on_device_inserted(device): ... print(f"New device detected: {device.model}") ... print(f"Path: {device.device_path}") ... print(f"Size: {device.size_bytes / (1024**3):.1f} GB") - ... + ... >>> # Start monitoring with read-only policy >>> future = disk.start_device_monitoring( - ... on_device_inserted, + ... on_device_inserted, ... disk.SecurityPolicy.READ_ONLY ... ) - >>> + >>> >>> # Let it run for a while >>> try: ... print("Monitoring for devices. Insert a USB drive...") @@ -551,11 +551,11 @@ and calls the provided callback when a device is inserted. Examples: >>> from atom.sysinfo import disk >>> import time - >>> + >>> >>> # Define a callback function >>> def on_device_inserted(device): - ... print(f"New device: {device.model} ({device.size_bytes / (1024**3):.1f} GB) - >>> # Use as a context manager + ... print(f"New device: {device.model} ({device.size_bytes / (1024**3):.1f} GB) + >>> # Use as a context manager >>> with disk.monitor_devices(on_device_inserted, disk.SecurityPolicy.READ_ONLY): ... print("Monitoring for devices. Insert a USB drive...") @@ -563,7 +563,7 @@ and calls the provided callback when a device is inserted. ... time.sleep(30) # Monitor for 30 seconds ... except KeyboardInterrupt: ... print("Monitoring stopped by user") - ... + ... >>> print("Monitoring stopped") )"); @@ -770,4 +770,4 @@ and calls the provided callback when a device is inserted. >>> if most_free: ... print(f"Most free space: {most_free.path} ({most_free.free_space / (1024**3):.1f} GB free) )"); -} \ No newline at end of file +} diff --git a/python/sysinfo/memory.cpp b/python/sysinfo/memory.cpp index 94524871..ada42bd9 100644 --- a/python/sysinfo/memory.cpp +++ b/python/sysinfo/memory.cpp @@ -411,18 +411,18 @@ with updated memory information. Examples: >>> from atom.sysinfo import memory >>> import time - >>> + >>> >>> # Define a callback function >>> def on_memory_update(info): ... print(f"Memory usage: {info.memory_load_percentage:.1f}%") ... print(f"Available: {info.available_physical_memory / (1024**3):.2f} GB") - ... + ... >>> # Start monitoring >>> memory.start_memory_monitoring(on_memory_update) - >>> + >>> >>> # Let it run for a while >>> time.sleep(10) - >>> + >>> >>> # Stop monitoring >>> memory.stop_memory_monitoring() )"); @@ -452,11 +452,11 @@ Retrieves a timeline of memory statistics over a specified duration. Examples: >>> from atom.sysinfo import memory >>> import datetime - >>> + >>> >>> # Get memory timeline for 1 minute >>> timeline = memory.get_memory_timeline(datetime.timedelta(minutes=1)) >>> print(f"Collected {len(timeline)} memory snapshots") - >>> + >>> >>> # Analyze the data >>> for i, snapshot in enumerate(timeline): ... print(f"Snapshot {i}: {snapshot.memory_load_percentage:.1f}% used") @@ -590,17 +590,17 @@ the provided callback with memory information updates. Examples: >>> from atom.sysinfo import memory >>> import time - >>> + >>> >>> # Define a callback function >>> def on_memory_update(info): ... print(f"Memory usage: {info.memory_load_percentage:.1f}%") ... print(f"Available: {info.available_physical_memory / (1024**3):.2f} GB") - ... + ... >>> # Use as a context manager >>> with memory.monitor_memory(on_memory_update): ... print("Monitoring memory for 5 seconds...") ... time.sleep(5) - ... + ... >>> print("Monitoring stopped") )"); @@ -749,4 +749,4 @@ the provided callback with memory information updates. >>> for i, usage in enumerate(history): ... print(f"Sample {i+1}: {usage:.1f}%") )"); -} \ No newline at end of file +} diff --git a/python/sysinfo/os.cpp b/python/sysinfo/os.cpp index fb408051..44b61974 100644 --- a/python/sysinfo/os.cpp +++ b/python/sysinfo/os.cpp @@ -496,4 +496,4 @@ name, version, kernel version, architecture, and more. >>> bits = os.get_architecture_bits() >>> print(f"Running on a {bits}-bit architecture") )"); -} \ No newline at end of file +} diff --git a/python/sysinfo/sysinfo_printer.cpp b/python/sysinfo/sysinfo_printer.cpp index d9f547a8..4fc8e380 100644 --- a/python/sysinfo/sysinfo_printer.cpp +++ b/python/sysinfo/sysinfo_printer.cpp @@ -33,7 +33,7 @@ PYBIND11_MODULE(sysinfo_printer, m) { R"(Formats and presents system information in human-readable formats. This class provides methods to format different types of system information -into readable text, generate comprehensive system reports, and export this +into readable text, generate comprehensive system reports, and export this information to various file formats like HTML, JSON, and Markdown. Examples: @@ -43,7 +43,7 @@ information to various file formats like HTML, JSON, and Markdown. >>> # Format it as readable text >>> formatted = sysinfo_printer.SystemInfoPrinter.format_cpu_info(cpu_info) >>> print(formatted) - >>> + >>> >>> # Generate a comprehensive system report >>> full_report = sysinfo_printer.SystemInfoPrinter.generate_full_report() >>> print(full_report) @@ -202,7 +202,7 @@ software components of the system. >>> # Generate a full system report >>> report = sysinfo_printer.SystemInfoPrinter.generate_full_report() >>> print(report) - >>> + >>> >>> # Optionally, save to a file >>> with open('system_report.txt', 'w') as f: ... f.write(report) @@ -650,11 +650,11 @@ Markdown file at the specified location. ... formats=["html", "markdown"], ... report_types=["performance", "security"] ... ) - >>> + >>> >>> # Check results >>> for report_type, formats in results.items(): ... print(f"{report_type} report:") ... for format_name, result in formats.items(): ... print(f" {format_name}: {result}") )"); -} \ No newline at end of file +} diff --git a/python/sysinfo/wifi.cpp b/python/sysinfo/wifi.cpp index 476b4387..bf5dae2a 100644 --- a/python/sysinfo/wifi.cpp +++ b/python/sysinfo/wifi.cpp @@ -584,27 +584,27 @@ including download/upload speeds, latency, packet loss, and signal strength. Examples: >>> from atom.sysinfo import wifi >>> import time - >>> + >>> >>> # Simple automatic monitoring for 20 seconds >>> with wifi.monitor_network(20, 2) as monitor: ... while monitor.is_active: ... print(f"Monitoring... {monitor.elapsed_time:.1f}s elapsed, " ... f"{monitor.remaining_time:.1f}s remaining") ... monitor.update() # This will sleep for the interval - ... + ... >>> # Get results after monitoring completes >>> avg_stats = monitor.average_stats >>> print(f"Average download: {avg_stats.download_speed:.2f} MB/s") >>> print(f"Average upload: {avg_stats.upload_speed:.2f} MB/s") >>> print(f"Average latency: {avg_stats.latency:.2f} ms") - >>> + >>> >>> # Manual updating >>> with wifi.monitor_network(30, 5) as monitor: ... # Do other things and manually update periodically ... for i in range(6): ... print(f"Taking measurement {i+1}") ... monitor.update() - ... + ... >>> print(f"Collected {len(monitor.stats_history)} measurements") )"); @@ -703,17 +703,17 @@ This is a simplified ping implementation for network diagnostics. >>> from atom.sysinfo import wifi >>> # Ping a host 5 times >>> results, summary = wifi.ping("www.example.com", 5) - >>> + >>> >>> # Print summary >>> print(f"Host: {summary['host']}") >>> print(f"Packets: {summary['packets_received']}/{summary['packets_sent']}") >>> print(f"Packet loss: {summary['packet_loss']:.1f}%") - >>> + >>> >>> if summary['packets_received'] > 0: ... print(f"Latency: min={summary['min_latency']:.1f}ms, " ... f"avg={summary['avg_latency']:.1f}ms, " ... f"max={summary['max_latency']:.1f}ms") - >>> + >>> >>> # Individual results >>> for i, result in enumerate(results): ... if result['success']: @@ -721,4 +721,4 @@ This is a simplified ping implementation for network diagnostics. ... else: ... print(f"Ping {i+1}: {result['error']} ") )"); -} \ No newline at end of file +} diff --git a/python/system/__init__.py b/python/system/__init__.py index 5e0221f1..06a59dcd 100644 --- a/python/system/__init__.py +++ b/python/system/__init__.py @@ -1 +1 @@ -# Auto-generated __init__.py for system module \ No newline at end of file +# Auto-generated __init__.py for system module diff --git a/python/system/command.cpp b/python/system/command.cpp index 265a37ec..1fa1062c 100644 --- a/python/system/command.cpp +++ b/python/system/command.cpp @@ -353,7 +353,7 @@ PYBIND11_MODULE(command, m) { Examples: >>> from atom.system import command >>> results = command.execute_commands_with_common_env( - ... ["echo %HOME%", "echo %PATH%"], + ... ["echo %HOME%", "echo %PATH%"], ... {"HOME": "/home/user", "PATH": "/usr/bin"} ... ) >>> for output, status in results: @@ -471,4 +471,4 @@ This class provides methods to store, retrieve, and search for commands that hav >>> history = command.create_command_history(100) >>> history.add_command("echo Hello World", 0) )"); -} \ No newline at end of file +} diff --git a/python/system/crash_quotes.cpp b/python/system/crash_quotes.cpp index 3b955b16..09121138 100644 --- a/python/system/crash_quotes.cpp +++ b/python/system/crash_quotes.cpp @@ -261,4 +261,4 @@ It can load quotes from and save them to JSON files. return !self.empty(); }, "Support for boolean evaluation."); -} \ No newline at end of file +} diff --git a/python/system/crontab.cpp b/python/system/crontab.cpp index edff07ff..c892ddce 100644 --- a/python/system/crontab.cpp +++ b/python/system/crontab.cpp @@ -364,22 +364,22 @@ This module provides classes for managing cron jobs in both memory and the syste Examples: >>> from atom.system.crontab import CronManager, CronJob - >>> + >>> >>> # Create a new cron manager >>> manager = CronManager() - >>> + >>> >>> # Create a job that runs every day at midnight >>> job = CronJob("0 0 * * *", "backup.sh", True, "backups", "Daily backup") - >>> + >>> >>> # Add the job to the manager >>> manager.create_cron_job(job) - >>> + >>> >>> # Validate a cron expression >>> result = CronManager.validate_cron_expression("0 0 * * *") >>> if result.valid: ... print("Valid cron expression") - >>> + >>> >>> # Export jobs to system crontab >>> manager.export_to_crontab() )"; -} \ No newline at end of file +} diff --git a/python/system/env.cpp b/python/system/env.cpp index fa3138e7..3c29740d 100644 --- a/python/system/env.cpp +++ b/python/system/env.cpp @@ -87,7 +87,7 @@ PYBIND11_MODULE(env, m) { std::shared_ptr>( m, "ScopedEnv", R"(Temporary environment variable scope. - + When this object is created, it sets the specified environment variable. When the object is destroyed, the original value is restored.)") .def(py::init(), @@ -792,4 +792,4 @@ and command-line arguments. >>> print(f"System: {info['system']} ({info['arch']}) ") >>> print(f"User: {info['user']} on {info['host']}") )"); -} \ No newline at end of file +} diff --git a/python/system/gpio.cpp b/python/system/gpio.cpp index 8344cab7..5ff60b1a 100644 --- a/python/system/gpio.cpp +++ b/python/system/gpio.cpp @@ -290,4 +290,4 @@ making it easier to work with related pins. Returns: The corresponding edge as a string. )"); -} \ No newline at end of file +} diff --git a/python/system/pidwatcher.cpp b/python/system/pidwatcher.cpp index ffd98b0f..dfd7ac59 100644 --- a/python/system/pidwatcher.cpp +++ b/python/system/pidwatcher.cpp @@ -849,4 +849,4 @@ process, stop monitoring, and switch the target process. >>> for proc in high_mem: ... print(f"{proc.name}: {proc.memory_usage/1024:.1f} MB") )"); -} \ No newline at end of file +} diff --git a/python/system/power.cpp b/python/system/power.cpp index 2c03c5c1..dc774066 100644 --- a/python/system/power.cpp +++ b/python/system/power.cpp @@ -154,4 +154,4 @@ This function ensures the brightness level is clamped between 0 and 100. >>> # Values outside the range are clamped to 0-100 >>> power.set_screen_brightness_safely(150) # Will set to 100 )"); -} \ No newline at end of file +} diff --git a/python/system/priority.cpp b/python/system/priority.cpp index e6a368f8..9221007f 100644 --- a/python/system/priority.cpp +++ b/python/system/priority.cpp @@ -136,14 +136,14 @@ Different policies provide different behaviors for thread execution. >>> import threading >>> # Set current thread to high priority >>> priority.set_thread_priority(priority.PriorityLevel.HIGHEST) - >>> + >>> >>> # Create thread and set its priority (using native handle) >>> def worker(): ... # Get native handle and set priority (platform-specific code) ... thread_handle = threading.get_native_id() # This is simplified ... priority.set_thread_priority(priority.PriorityLevel.ABOVE_NORMAL, thread_handle) ... # Thread work... - ... + ... >>> t = threading.Thread(target=worker) >>> t.start() )"); @@ -232,14 +232,14 @@ Different policies provide different behaviors for thread execution. Examples: >>> from atom.system import priority >>> import time - >>> + >>> >>> # Callback function for priority changes >>> def on_priority_change(level): ... print(f"Process priority changed to: {level}") - ... + ... >>> # Monitor process 1234 for priority changes >>> priority.start_priority_monitor(1234, on_priority_change) - >>> + >>> >>> # Keep the program running to receive callbacks >>> try: ... while True: @@ -312,7 +312,7 @@ Different policies provide different behaviors for thread execution. >>> from atom.system import priority >>> cpu_count = priority.get_available_cpu_count() >>> print(f"This system has {cpu_count} CPU cores") - >>> + >>> >>> # Pin process to first half of available cores >>> first_half = list(range(cpu_count // 2)) >>> priority.set_process_affinity(first_half) @@ -400,7 +400,7 @@ priority and restores it when the context is exited. Examples: >>> from atom.system import priority >>> import time - >>> + >>> >>> # Temporarily run with high priority >>> with priority.thread_priority(priority.PriorityLevel.HIGHEST): ... # This code runs with high priority @@ -463,7 +463,7 @@ priority and restores it when the context is exited. Examples: >>> from atom.system import priority >>> import time - >>> + >>> >>> # Temporarily run with high priority >>> with priority.process_priority(priority.PriorityLevel.HIGHEST): ... # This code runs with high priority @@ -506,16 +506,16 @@ priority and restores it when the context is exited. Examples: >>> from atom.system import priority - >>> + >>> >>> def compute_something(): ... result = 0 ... for i in range(10000000): ... result += i ... return result - ... + ... >>> # Run with high priority >>> result = priority.run_with_priority( - ... priority.PriorityLevel.HIGHEST, + ... priority.PriorityLevel.HIGHEST, ... compute_something ... ) >>> print(f"Result: {result}") @@ -581,21 +581,21 @@ priority and restores it when the context is exited. Examples: >>> from atom.system import priority >>> import threading - >>> + >>> >>> def worker(cpu_id): ... # Pin this thread to the specified CPU ... priority.pin_thread_to_cpus([cpu_id]) ... # Now this thread will only run on the specified CPU ... for i in range(10): ... print(f"Thread on CPU {cpu_id}: {i}") - ... + ... >>> # Create threads and pin each to a different CPU >>> threads = [] >>> for i in range(4): # Create 4 threads ... t = threading.Thread(target=worker, args=(i,)) ... threads.append(t) ... t.start() - ... + ... >>> # Wait for all threads to complete >>> for t in threads: ... t.join() @@ -646,4 +646,4 @@ priority and restores it when the context is exited. >>> cpu_ids = priority.get_thread_affinity() >>> print(f"Current thread can run on these CPUs: {cpu_ids}") )"); -} \ No newline at end of file +} diff --git a/python/system/process.cpp b/python/system/process.cpp index eaeee478..27bc1d29 100644 --- a/python/system/process.cpp +++ b/python/system/process.cpp @@ -1190,4 +1190,4 @@ with the specified command. This function is only available on Windows. ... time.sleep(10) # Wait for events ... # Monitoring stops automatically when leaving the block )"); -} \ No newline at end of file +} diff --git a/python/system/process_info.cpp b/python/system/process_info.cpp index 5a7b2ddc..5337163e 100644 --- a/python/system/process_info.cpp +++ b/python/system/process_info.cpp @@ -154,7 +154,7 @@ including protocol, local and remote addresses, ports, and connection status. }); // FileDescriptor struct binding - py::class_(m, "FileDescriptor", + py::class_(m, "FileDescriptor", R"(Represents a file descriptor or handle used by a process. This structure contains information about file descriptors opened by a process, @@ -171,13 +171,13 @@ including file descriptor ID, file path, type, and access mode. >>> print(f"FD {fd.fd}: {fd.path} ({fd.type}, {fd.mode}) ") )") .def(py::init<>()) - .def_readwrite("fd", &atom::system::FileDescriptor::fd, + .def_readwrite("fd", &atom::system::FileDescriptor::fd, "File descriptor/handle ID") - .def_readwrite("path", &atom::system::FileDescriptor::path, + .def_readwrite("path", &atom::system::FileDescriptor::path, "File path") - .def_readwrite("type", &atom::system::FileDescriptor::type, + .def_readwrite("type", &atom::system::FileDescriptor::type, "File type (regular, socket, pipe, etc.)") - .def_readwrite("mode", &atom::system::FileDescriptor::mode, + .def_readwrite("mode", &atom::system::FileDescriptor::mode, "Access mode (r, w, rw, etc.)") .def("__repr__", [](const atom::system::FileDescriptor& fd) { return ">> print(f"Sample process: {sample.name} (PID: {sample.pid}) ") >>> print(f"CPU: {sample.resources.cpu_usage}%, Memory: {sample.resources.mem_usage / 1024 / 1024} MB") )"); -} \ No newline at end of file +} diff --git a/python/system/process_manager.cpp b/python/system/process_manager.cpp index f931cf9d..16739ba8 100644 --- a/python/system/process_manager.cpp +++ b/python/system/process_manager.cpp @@ -484,4 +484,4 @@ This function returns a context manager that automatically handles process creat } throw py::key_error("Invalid key: " + name); }); -} \ No newline at end of file +} diff --git a/python/system/registry.cpp b/python/system/registry.cpp index ae0aab71..565139bd 100644 --- a/python/system/registry.cpp +++ b/python/system/registry.cpp @@ -434,4 +434,4 @@ and event callbacks. >>> if registry.is_success(result): ... print("Registry initialized successfully") )"); -} \ No newline at end of file +} diff --git a/python/system/signal.cpp b/python/system/signal.cpp index 11ac43b1..e1fb17d2 100644 --- a/python/system/signal.cpp +++ b/python/system/signal.cpp @@ -67,13 +67,13 @@ PYBIND11_MODULE(signal, m) { m, "SignalHandlerRegistry", R"(Singleton class to manage signal handlers and dispatch signals. -This class handles registering and dispatching signal handlers with priorities. +This class handles registering and dispatching signal handlers with priorities. It also provides a mechanism to set up default crash signal handlers. Examples: >>> from atom.system import signal >>> registry = signal.SignalHandlerRegistry.get_instance() - >>> + >>> >>> # Define a simple handler >>> def handle_interrupt(sig_id): ... print(f"Received interrupt signal: {sig_id}") @@ -90,7 +90,7 @@ It also provides a mechanism to set up default crash signal handlers. Reference to the singleton SignalHandlerRegistry instance. )") */ - + .def( "set_signal_handler", &SignalHandlerRegistry::setSignalHandler, py::arg("signal"), py::arg("handler"), py::arg("priority") = 0, @@ -257,7 +257,7 @@ in a separate thread to ensure thread safety and avoid blocking signal handling. Examples: >>> from atom.system import signal >>> manager = signal.SafeSignalManager.get_instance() - >>> + >>> >>> # Define a signal handler function >>> def handle_signal(sig_id): ... print(f"Handled signal {sig_id} safely in separate thread") @@ -294,7 +294,7 @@ in a separate thread to ensure thread safety and avoid blocking signal handling. Examples: >>> def safe_handler(sig_id): ... print(f"Safe handling of signal {sig_id}") - ... + ... >>> handler_id = manager.add_safe_signal_handler(15, safe_handler) )") .def("remove_safe_signal_handler_by_id", @@ -518,4 +518,4 @@ in a separate thread to ensure thread safety and avoid blocking signal handling. >>> registry = signal.SignalHandlerRegistry.get_instance() >>> registry.set_signal_handler(signal.SIGTERM, handler) )"); -} \ No newline at end of file +} diff --git a/python/system/signal_monitor.cpp b/python/system/signal_monitor.cpp index e7d76b5f..9a630ae4 100644 --- a/python/system/signal_monitor.cpp +++ b/python/system/signal_monitor.cpp @@ -127,21 +127,21 @@ and register callbacks for various signal events. Examples: >>> from atom.system import signal_monitor >>> import time - >>> + >>> >>> # Get the singleton instance >>> monitor = signal_monitor.get_instance() - >>> + >>> >>> # Start monitoring all signals >>> monitor.start() - >>> + >>> >>> # Wait a bit to collect stats >>> time.sleep(5) - >>> + >>> >>> # Get a snapshot of signal statistics >>> stats = monitor.get_stat_snapshot() >>> for signal_id, signal_stats in stats.items(): ... print(f"Signal {signal_id}: Received {signal_stats.received}") - >>> + >>> >>> # Stop monitoring >>> monitor.stop() )") @@ -165,7 +165,7 @@ and register callbacks for various signal events. >>> # Start monitoring all signals, checking every 500ms >>> monitor = signal_monitor.get_instance() >>> monitor.start(500) - >>> + >>> >>> # Or monitor specific signals >>> import signal >>> monitor.start(1000, [signal.SIGINT, signal.SIGTERM]) @@ -219,12 +219,12 @@ and register callbacks for various signal events. Examples: >>> from atom.system import signal_monitor >>> import signal - >>> + >>> >>> # Define a callback function >>> def on_signal_threshold(signal_id, stats): ... print(f"Signal {signal_id} threshold exceeded!") ... print(f"Received: {stats.received}, Errors: {stats.handler_errors}") - ... + ... >>> # Register callback for SIGINT - triggered after 5 occurrences >>> monitor = signal_monitor.get_instance() >>> callback_id = monitor.add_threshold_callback( @@ -267,12 +267,12 @@ and register callbacks for various signal events. >>> from atom.system import signal_monitor >>> import signal >>> import time - >>> + >>> >>> # Define a callback function >>> def on_signal_inactivity(signal_id, stats): ... print(f"Signal {signal_id} has been inactive for too long!") ... print(f"Last received: {stats.last_received}") - ... + ... >>> # Register callback for SIGTERM - triggered after 30 seconds of inactivity >>> monitor = signal_monitor.get_instance() >>> callback_id = monitor.add_inactivity_callback( @@ -293,15 +293,15 @@ and register callbacks for various signal events. Examples: >>> from atom.system import signal_monitor >>> monitor = signal_monitor.get_instance() - >>> + >>> >>> # Add a callback >>> def callback(signal_id, stats): ... print(f"Signal {signal_id} event") - ... + ... >>> callback_id = monitor.add_threshold_callback( ... signal.SIGINT, 5, 0, callback ... ) - >>> + >>> >>> # Later, remove the callback >>> success = monitor.remove_callback(callback_id) >>> print(f"Callback removed: {success}") @@ -315,7 +315,7 @@ and register callbacks for various signal events. Examples: >>> from atom.system import signal_monitor >>> monitor = signal_monitor.get_instance() - >>> + >>> >>> # Get stats for all monitored signals >>> stats = monitor.get_stat_snapshot() >>> for signal_id, signal_stats in stats.items(): @@ -333,7 +333,7 @@ and register callbacks for various signal events. Examples: >>> from atom.system import signal_monitor >>> monitor = signal_monitor.get_instance() - >>> + >>> >>> # Get list of monitored signals >>> signals = monitor.get_monitored_signals() >>> print(f"Monitoring {len(signals)} signals: {signals}") @@ -344,7 +344,7 @@ and register callbacks for various signal events. Examples: >>> from atom.system import signal_monitor >>> monitor = signal_monitor.get_instance() - >>> + >>> >>> # Reset all stats to zero >>> monitor.reset_all_stats() >>> print("All signal statistics have been reset") @@ -390,7 +390,7 @@ This is a convenience function to get the SignalMonitor instance and start it. >>> from atom.system import signal_monitor >>> # Start monitoring all signals >>> signal_monitor.start_monitoring() - >>> + >>> >>> # Or monitor specific signals with custom interval >>> import signal >>> signal_monitor.start_monitoring(500, [signal.SIGINT, signal.SIGTERM]) @@ -519,17 +519,17 @@ and removes the monitoring when the context is exited. Examples: >>> from atom.system import signal_monitor >>> import signal - >>> + >>> >>> def on_signal_event(signal_id, stats): ... print(f"Signal {signal_id} event detected!") - ... + ... >>> # Use as a context manager to monitor signals >>> with signal_monitor.monitor_signals( ... [signal.SIGINT, signal.SIGTERM], on_signal_event, 500 ... ): ... print("Monitoring signals in this block...") ... # Your code here - ... + ... >>> print("Signal monitoring stopped") )"); @@ -602,19 +602,19 @@ and removes the monitoring when the context is exited. >>> import threading >>> import os >>> import time - >>> + >>> >>> # Set up a thread to send a signal after 1 second >>> def send_test_signal(pid, sig_to_send): ... time.sleep(1) ... os.kill(pid, sig_to_send) - ... + ... >>> # Note: SIGUSR1 might not be available on Windows without specific setup. >>> # Using SIGINT for a more portable example, though be careful with terminal interruption. >>> # For a real test, use a signal like SIGUSR1 if available and handled. >>> test_signal = signal.SIGUSR1 if hasattr(signal, "SIGUSR1") else signal.SIGINT >>> pid = os.getpid() >>> threading.Thread(target=send_test_signal, args=(pid, test_signal)).start() - >>> + >>> >>> # Wait for the signal with 2 second timeout >>> print(f"Waiting for signal {test_signal}...") >>> if signal_monitor.wait_for_signal(test_signal, 2000): @@ -779,11 +779,11 @@ context is entered until get_rate() is called. >>> import signal >>> import time >>> import os - >>> + >>> >>> # Note: SIGUSR1 might not be available on Windows. >>> test_signal = signal.SIGUSR1 if hasattr(signal, "SIGUSR1") else signal.SIGINT >>> pid = os.getpid() - >>> + >>> >>> # Use as a context manager to track signal rate >>> with signal_monitor.track_signal_rate(test_signal) as tracker: ... # Generate some signals @@ -800,9 +800,9 @@ context is entered until get_rate() is called. ... sig_thread.start() ... time.sleep(0.6) // Allow signals to be sent and processed ... sig_thread.join() - ... + ... ... // Get the rate ... rate = tracker.get_rate() ... print(f"Signal rate for {test_signal}: {rate:.2f} signals per second") )"); -} \ No newline at end of file +} diff --git a/python/system/signal_utils.cpp b/python/system/signal_utils.cpp index 1bcfd8d6..a3cbf300 100644 --- a/python/system/signal_utils.cpp +++ b/python/system/signal_utils.cpp @@ -70,7 +70,7 @@ they're properly cleaned up when the object goes out of scope. >>> def handle_sigint(signal_id): ... print(f"Caught signal {signal_utils.get_signal_name(signal_id)}") ... return True # Continue handling - ... + ... >>> # Create a scoped handler for SIGINT >>> handler = signal_utils.ScopedSignalHandler(signal_utils.SIGINT, handle_sigint) >>> # The handler will be automatically removed when it goes out of scope @@ -96,19 +96,19 @@ When the group is destroyed, all its handlers are automatically removed. >>> from atom.system import signal_utils >>> # Create a signal group >>> group = signal_utils.SignalGroup("app_signals") - >>> + >>> >>> def handle_int(signal_id): ... print("Handling SIGINT") ... return True - ... + ... >>> def handle_term(signal_id): ... print("Handling SIGTERM") ... return True - ... + ... >>> # Add handlers to the group >>> group.add_handler(signal_utils.SIGINT, handle_int) >>> group.add_handler(signal_utils.SIGTERM, handle_term) - >>> + >>> >>> # All handlers will be removed when group is destroyed )") .def(py::init(), py::arg("group_name") = "", @@ -144,11 +144,11 @@ When the group is destroyed, all its handlers are automatically removed. Examples: >>> from atom.system import signal_utils >>> group = signal_utils.SignalGroup("app_signals") - >>> + >>> >>> def handle_signal(signal_id): ... print(f"Handling signal {signal_id}") ... return True - ... + ... >>> handler_id = group.add_handler(signal_utils.SIGINT, handle_signal) >>> print(f"Registered handler with ID: {handler_id}") )") @@ -165,11 +165,11 @@ When the group is destroyed, all its handlers are automatically removed. Examples: >>> from atom.system import signal_utils >>> group = signal_utils.SignalGroup() - >>> + >>> >>> def handle_signal(signal_id): ... print(f"Handling signal {signal_id}") ... return True - ... + ... >>> handler_id = group.add_handler(signal_utils.SIGINT, handle_signal) >>> # Later, when we want to remove just this handler: >>> success = group.remove_handler(handler_id) @@ -188,11 +188,11 @@ When the group is destroyed, all its handlers are automatically removed. Examples: >>> from atom.system import signal_utils >>> group = signal_utils.SignalGroup() - >>> + >>> >>> # Add multiple handlers for SIGINT >>> group.add_handler(signal_utils.SIGINT, lambda sig: True) >>> group.add_handler(signal_utils.SIGINT, lambda sig: True) - >>> + >>> >>> # Remove all SIGINT handlers >>> removed = group.remove_signal_handlers(signal_utils.SIGINT) >>> print(f"Removed {removed} handlers") @@ -206,11 +206,11 @@ When the group is destroyed, all its handlers are automatically removed. Examples: >>> from atom.system import signal_utils >>> group = signal_utils.SignalGroup() - >>> + >>> >>> # Add handlers for different signals >>> group.add_handler(signal_utils.SIGINT, lambda sig: True) >>> group.add_handler(signal_utils.SIGTERM, lambda sig: True) - >>> + >>> >>> # Later, remove all handlers >>> removed = group.remove_all() >>> print(f"Removed {removed} handlers") @@ -224,10 +224,10 @@ When the group is destroyed, all its handlers are automatically removed. Examples: >>> from atom.system import signal_utils >>> group = signal_utils.SignalGroup() - >>> + >>> >>> group.add_handler(signal_utils.SIGINT, lambda sig: True) >>> group.add_handler(signal_utils.SIGTERM, lambda sig: True) - >>> + >>> >>> handler_ids = group.get_handler_ids() >>> for signal, ids in handler_ids.items(): ... signal_name = signal_utils.get_signal_name(signal) @@ -262,11 +262,11 @@ When the group is destroyed, all its handlers are automatically removed. >>> from atom.system import signal_utils >>> # Create a signal group >>> group = signal_utils.make_signal_group("app_signals") - >>> + >>> >>> def handle_signal(signal_id): ... print(f"Handling signal {signal_id}") ... return True - ... + ... >>> group.add_handler(signal_utils.SIGINT, handle_signal) >>> # The group will be automatically cleaned up when the reference is lost )", @@ -308,12 +308,12 @@ When the group is destroyed, all its handlers are automatically removed. Examples: >>> from atom.system import signal_utils >>> import time - >>> + >>> >>> def critical_section(): ... print("Starting critical section (SIGINT blocked) ") ... time.sleep(2) # During this time, SIGINT is blocked ... print("Ending critical section") - ... + ... >>> # SIGINT will be blocked during the execution of critical_section >>> signal_utils.with_blocked_signal(signal_utils.SIGINT, critical_section) )"); @@ -383,16 +383,16 @@ When the group is destroyed, all its handlers are automatically removed. Examples: >>> from atom.system import signal_utils >>> import time - >>> + >>> >>> def handle_int(signal_id): ... print("Got SIGINT, but continuing execution") ... return True - ... + ... >>> # Use as a context manager >>> with signal_utils.handle_signal(signal_utils.SIGINT, handle_int): ... print("SIGINT will be handled specially in this block") ... time.sleep(5) # Try pressing Ctrl+C during this time - ... + ... >>> print("Back to normal signal handling") )"); @@ -469,13 +469,13 @@ When the group is destroyed, all its handlers are automatically removed. Examples: >>> from atom.system import signal_utils >>> import time - >>> + >>> >>> # Use as a context manager to block SIGINT >>> with signal_utils.block_signal(signal_utils.SIGINT): ... print("SIGINT is blocked in this block") ... print("Try pressing Ctrl+C, it won't interrupt until after the block") ... time.sleep(5) - ... + ... >>> print("SIGINT is now unblocked") )"); @@ -548,15 +548,15 @@ When the group is destroyed, all its handlers are automatically removed. Examples: >>> from atom.system import signal_utils - >>> + >>> >>> def handle_signal(signal_id): ... signal_name = signal_utils.get_signal_name(signal_id) ... print(f"Handling {signal_name}") ... return True - ... + ... >>> # Single signal handler >>> int_handler = signal_utils.create_handler(signal_utils.SIGINT, handler=handle_signal) - >>> + >>> >>> # Multiple signal handler group >>> termination_handlers = signal_utils.create_handler( ... [signal_utils.SIGTERM, signal_utils.SIGINT, signal_utils.SIGQUIT], @@ -628,10 +628,10 @@ When the group is destroyed, all its handlers are automatically removed. Examples: >>> from atom.system import signal_utils >>> import threading, time, os - >>> + >>> >>> # Ensure SIGUSR1 is available for the example >>> sig_to_test = signal_utils.SIGUSR1 if hasattr(signal_utils, "SIGUSR1") else signal_utils.SIGINT - >>> + >>> >>> def send_signal_thread_func(pid, sig): ... time.sleep(0.5) # Give capture_next_signal time to set up ... try: @@ -639,10 +639,10 @@ When the group is destroyed, all its handlers are automatically removed. ... print(f"Test thread: Sent signal {sig}") ... except Exception as e: ... print(f"Test thread: Error sending signal: {e}") - ... + ... >>> t = threading.Thread(target=send_signal_thread_func, args=(os.getpid(), sig_to_test)) >>> t.start() - >>> + >>> >>> print(f"Main thread: Waiting for signal {sig_to_test}...") >>> success, sig = signal_utils.capture_next_signal(sig_to_test, 2.0) >>> if success and sig is not None: @@ -765,4 +765,4 @@ When the group is destroyed, all its handlers are automatically removed. >>> for sig_id in available_sigs: ... print(signal_utils.get_signal_name(sig_id)) )"); -} \ No newline at end of file +} diff --git a/python/system/stat.cpp b/python/system/stat.cpp index d8fe6cd3..15e58885 100644 --- a/python/system/stat.cpp +++ b/python/system/stat.cpp @@ -414,7 +414,7 @@ constructor. >>> # Format the modification time >>> formatted_time = stat.Stat.format_time(s.mtime()) >>> print(f"Last modified: {formatted_time}") - >>> + >>> >>> # Custom time format >>> custom_format = stat.Stat.format_time(s.mtime(), "%H:%M:%S %d-%m-%Y") >>> print(f"Last modified: {custom_format}") @@ -769,4 +769,4 @@ to work with Stat objects. ... print(f"File size: {s.size()} bytes") ... print(f"Last modified: {stat.Stat.format_time(s.mtime())}") )"); -} \ No newline at end of file +} diff --git a/python/system/storage.cpp b/python/system/storage.cpp index d75dafb6..7f0f32e5 100644 --- a/python/system/storage.cpp +++ b/python/system/storage.cpp @@ -36,14 +36,14 @@ trigger registered callback functions when storage space changes. >>> from atom.system import storage >>> # Create a storage monitor >>> monitor = storage.StorageMonitor() - >>> + >>> >>> # Define a callback function >>> def on_storage_change(path): ... print(f"Storage change detected at: {path}") - ... + ... >>> # Register the callback >>> monitor.register_callback(on_storage_change) - >>> + >>> >>> # Start monitoring >>> monitor.start_monitoring() )") @@ -296,12 +296,12 @@ and then waits for the specified interval before returning. >>> from atom.system import storage >>> # Create a polling callback factory with 2-second interval >>> polling = storage.with_polling_callback(2.0) - >>> + >>> >>> # Use it to decorate our actual callback >>> @polling ... def my_callback(path): ... print(f"Storage changed: {path}") - ... + ... >>> # Register the decorated callback >>> monitor = storage.StorageMonitor() >>> monitor.register_callback(my_callback) @@ -363,7 +363,7 @@ the context and stops it when exiting. >>> from atom.system import storage >>> def notify_change(path): ... print(f"Storage changed: {path}") - ... + ... >>> # Use as a context manager >>> with storage.monitor_storage(notify_change): ... print("Monitoring storage...") @@ -372,4 +372,4 @@ the context and stops it when exiting. ... time.sleep(10) ... # Monitoring automatically stops when exiting the context )"); -} \ No newline at end of file +} diff --git a/python/system/user.cpp b/python/system/user.cpp index a32e3654..aa812e1f 100644 --- a/python/system/user.cpp +++ b/python/system/user.cpp @@ -427,4 +427,4 @@ current process. ... assert debug == "1" >>> # Original environment is restored after the with block )"); -} \ No newline at end of file +} diff --git a/python/system/voltage.cpp b/python/system/voltage.cpp index 8b692023..38096ef3 100644 --- a/python/system/voltage.cpp +++ b/python/system/voltage.cpp @@ -554,7 +554,7 @@ and power source information and calls the provided callback when changes are de Examples: >>> from atom.system import voltage >>> import time - >>> + >>> >>> # Define a callback function >>> def on_voltage_change(input_v, battery_v, sources): ... print(f"Voltage change detected!") @@ -564,12 +564,12 @@ and power source information and calls the provided callback when changes are de ... print(f"Battery voltage: {battery_v} V") ... for source in sources: ... print(f"Source: {source.name}, Type: {source.type}") - ... + ... >>> # Use as a context manager >>> with voltage.monitor_voltage_changes(0.5, on_voltage_change): ... print("Monitoring voltage changes for 10 seconds...") ... time.sleep(10) - ... + ... >>> print("Monitoring stopped") )"); -} \ No newline at end of file +} diff --git a/python/system/wregistry.cpp b/python/system/wregistry.cpp index ecf58b6c..8926a8b1 100644 --- a/python/system/wregistry.cpp +++ b/python/system/wregistry.cpp @@ -108,7 +108,7 @@ PYBIND11_MODULE(wregistry, m) { >>> from atom.system import wregistry >>> # Get values from HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run >>> values = wregistry.get_registry_values( - ... wregistry.HKEY_CURRENT_USER, + ... wregistry.HKEY_CURRENT_USER, ... "Software\\Microsoft\\Windows\\CurrentVersion\\Run" ... ) >>> for name, value in values.items(): @@ -863,4 +863,4 @@ This function prints all matching values to standard output. ... except ValueError as e: ... print(f"Error: {e}") )"); -} \ No newline at end of file +} diff --git a/python/type/__init__.py b/python/type/__init__.py index c9d5c5d5..d1f72b61 100644 --- a/python/type/__init__.py +++ b/python/type/__init__.py @@ -1 +1 @@ -# Auto-generated __init__.py for type module \ No newline at end of file +# Auto-generated __init__.py for type module diff --git a/python/type/expected.cpp b/python/type/expected.cpp index 4ce49c92..f979e89d 100644 --- a/python/type/expected.cpp +++ b/python/type/expected.cpp @@ -508,4 +508,4 @@ PYBIND11_MODULE(expected, m) { >>> exp.error() 'Something went wrong' )"); -} \ No newline at end of file +} diff --git a/python/type/json_schema.cpp b/python/type/json_schema.cpp index 504daf11..855b3128 100644 --- a/python/type/json_schema.cpp +++ b/python/type/json_schema.cpp @@ -30,7 +30,7 @@ PYBIND11_MODULE(json_schema, m) { // Bind SchemaVersion enum py::enum_(m, "SchemaVersion", R"( JSON Schema specification versions. - + Enum values: DRAFT4: JSON Schema draft 4 DRAFT6: JSON Schema draft 6 @@ -50,7 +50,7 @@ PYBIND11_MODULE(json_schema, m) { // Bind ValidationError struct py::class_(m, "ValidationError", R"( Structure representing a JSON Schema validation error. - + Attributes: message (str): Error message describing the validation failure path (str): JSON path to the location where validation failed @@ -78,7 +78,7 @@ PYBIND11_MODULE(json_schema, m) { // Bind ValidationOptions struct py::class_(m, "ValidationOptions", R"( Configuration options for JSON Schema validation. - + Attributes: fail_fast (bool): Stop on first error validate_schema (bool): Validate schema against meta-schema @@ -107,10 +107,10 @@ PYBIND11_MODULE(json_schema, m) { // Bind the JsonValidator class py::class_(m, "JsonValidator", R"( Enhanced JSON Schema validator with full JSON Schema draft support. - + This class provides methods for validating JSON instances against JSON Schemas following various draft versions of the specification. - + Args: options: Validation options @@ -163,7 +163,7 @@ PYBIND11_MODULE(json_schema, m) { Args: format_name: Name of the format - validator: Function that validates strings against this format. + validator: Function that validates strings against this format. Should take a string and return a boolean. )") .def("set_schema_manager", &JsonValidator::setSchemaManager, @@ -180,9 +180,9 @@ PYBIND11_MODULE(json_schema, m) { py::class_>( m, "SchemaManager", R"( Schema Manager for handling multiple schemas and references. - + This class manages multiple JSON schemas and resolves references between them. - + Args: options: Validation options to use for schemas @@ -338,4 +338,4 @@ PYBIND11_MODULE(json_schema, m) { ... } >>> manager = create_schema_manager(schemas) )"); -} \ No newline at end of file +} diff --git a/python/type/robin_hood.cpp b/python/type/robin_hood.cpp index 172c0246..fa3d9174 100644 --- a/python/type/robin_hood.cpp +++ b/python/type/robin_hood.cpp @@ -211,4 +211,4 @@ PYBIND11_MODULE(robin_hood, m) { >>> map["key"] 'value' )"); -} \ No newline at end of file +} diff --git a/python/type/trackable.cpp b/python/type/trackable.cpp index 401260da..32d32269 100644 --- a/python/type/trackable.cpp +++ b/python/type/trackable.cpp @@ -235,11 +235,11 @@ PYBIND11_MODULE(trackable, m) { >>> t.value += 10 >>> print(t.value) 52 - >>> + >>> >>> # With change callback >>> def on_change(old, new): ... print(f"Value changed from {old} to {new}") - ... + ... >>> t.subscribe(on_change) >>> t.value = 100 # This will trigger the callback )", @@ -286,4 +286,4 @@ PYBIND11_MODULE(trackable, m) { >>> supports_operation(t_int, "+") # Returns True >>> supports_operation(t_str, "*") # Returns False )"); -} \ No newline at end of file +} diff --git a/python/utils/__init__.py b/python/utils/__init__.py index 58ef7aff..8f304395 100644 --- a/python/utils/__init__.py +++ b/python/utils/__init__.py @@ -1 +1 @@ -# Auto-generated __init__.py for utils module \ No newline at end of file +# Auto-generated __init__.py for utils module diff --git a/python/utils/aes.cpp b/python/utils/aes.cpp index 3a1693eb..ce2896ea 100644 --- a/python/utils/aes.cpp +++ b/python/utils/aes.cpp @@ -193,4 +193,4 @@ PYBIND11_MODULE(aes, m) { >>> from atom.utils import aes >>> hash_value = aes.calculate_sha512("hello world") )"); -} \ No newline at end of file +} diff --git a/python/utils/bit.cpp b/python/utils/bit.cpp index f7416a7f..a796bd2c 100644 --- a/python/utils/bit.cpp +++ b/python/utils/bit.cpp @@ -364,4 +364,4 @@ PYBIND11_MODULE(bit, m) { >>> data = array.array('B', [0xFF, 0x0F, 0xF0, 0x00]) >>> result = bit.parallel_bit_operation(data, "count") )"); -} \ No newline at end of file +} diff --git a/python/utils/difflib.cpp b/python/utils/difflib.cpp index 9f59b8d1..82444dbf 100644 --- a/python/utils/difflib.cpp +++ b/python/utils/difflib.cpp @@ -250,4 +250,4 @@ and 1 means identical sequences. >>> difflib.get_close_matches("appel", ["ape", "apple", "peach", "puppy"]) ['apple', 'ape'] )"); -} \ No newline at end of file +} diff --git a/python/utils/error_stack.cpp b/python/utils/error_stack.cpp index 89fa0a41..2c00c2d6 100644 --- a/python/utils/error_stack.cpp +++ b/python/utils/error_stack.cpp @@ -227,23 +227,23 @@ filtering errors by module or severity, and exporting error data. Examples: >>> from atom.utils import error_stack >>> from atom.utils.error_stack import ErrorLevel, ErrorCategory - >>> + >>> >>> # Create an error stack >>> stack = error_stack.ErrorStack() - >>> + >>> >>> # Insert a simple error >>> stack.insert_error("File not found", "IO", "readFile", 42, "file_io.cpp") - >>> + >>> >>> # Insert an error with additional information >>> stack.insert_error_with_level( ... "Connection timeout", "Network", "connect", 123, "network.cpp", ... ErrorLevel.ERROR, ErrorCategory.NETWORK, 408) - >>> + >>> >>> # Get the latest error >>> latest = stack.get_latest_error() >>> if latest: ... print(f"Latest error: {latest.error_message} in {latest.module_name}") - >>> + >>> >>> # Export errors to JSON >>> json_data = stack.export_to_json() )") @@ -392,4 +392,4 @@ filtering errors by module or severity, and exporting error data. // Version information m.attr("__version__") = "1.0.0"; -} \ No newline at end of file +} diff --git a/python/utils/lcg.cpp b/python/utils/lcg.cpp index 424a0526..dbea2998 100644 --- a/python/utils/lcg.cpp +++ b/python/utils/lcg.cpp @@ -55,7 +55,7 @@ random numbers following different distributions. Args: filename: The name of the file to save the state to. - + Raises: RuntimeError: If the file cannot be opened. )") @@ -64,7 +64,7 @@ random numbers following different distributions. Args: filename: The name of the file to load the state from. - + Raises: RuntimeError: If the file cannot be opened or is corrupt. )") @@ -75,10 +75,10 @@ random numbers following different distributions. Args: min: The minimum value (inclusive). Defaults to 0. max: The maximum value (inclusive). Defaults to the maximum value of int. - + Returns: A random integer within the specified range. - + Raises: ValueError: If min > max. )") @@ -89,10 +89,10 @@ random numbers following different distributions. Args: min: The minimum value (inclusive). Defaults to 0.0. max: The maximum value (exclusive). Defaults to 1.0. - + Returns: A random double within the specified range. - + Raises: ValueError: If min >= max. )") @@ -103,10 +103,10 @@ random numbers following different distributions. Args: probability: The probability of returning true. Defaults to 0.5. - + Returns: A random boolean value. - + Raises: ValueError: If probability is not in [0,1]. )") @@ -118,10 +118,10 @@ random numbers following different distributions. Args: mean: The mean of the distribution. Defaults to 0.0. stddev: The standard deviation of the distribution. Defaults to 1.0. - + Returns: A random number following a Gaussian distribution. - + Raises: ValueError: If stddev <= 0. )") @@ -131,10 +131,10 @@ random numbers following different distributions. Args: lambda: The rate parameter (lambda) of the distribution. Defaults to 1.0. - + Returns: A random number following a Poisson distribution. - + Raises: ValueError: If lambda <= 0. )") @@ -145,10 +145,10 @@ random numbers following different distributions. Args: lambda: The rate parameter (lambda) of the distribution. Defaults to 1.0. - + Returns: A random number following an Exponential distribution. - + Raises: ValueError: If lambda <= 0. )") @@ -158,10 +158,10 @@ random numbers following different distributions. Args: probability: The probability of success in each trial. Defaults to 0.5. - + Returns: A random number following a Geometric distribution. - + Raises: ValueError: If probability not in (0,1). )") @@ -172,10 +172,10 @@ random numbers following different distributions. Args: shape: The shape parameter of the distribution. scale: The scale parameter of the distribution. Defaults to 1.0. - + Returns: A random number following a Gamma distribution. - + Raises: ValueError: If shape or scale <= 0. )") @@ -186,10 +186,10 @@ random numbers following different distributions. Args: alpha: The alpha parameter of the distribution. beta: The beta parameter of the distribution. - + Returns: A random number following a Beta distribution. - + Raises: ValueError: If alpha or beta <= 0. )") @@ -199,10 +199,10 @@ random numbers following different distributions. Args: degrees_of_freedom: The degrees of freedom of the distribution. - + Returns: A random number following a Chi-Squared distribution. - + Raises: ValueError: If degrees_of_freedom <= 0. )") @@ -215,10 +215,10 @@ random numbers following different distributions. total: The total number of items. success: The number of successful items. draws: The number of draws. - + Returns: A random number following a Hypergeometric distribution. - + Raises: ValueError: If parameters are invalid. )") @@ -230,10 +230,10 @@ random numbers following different distributions. Args: weights: The weights of the discrete distribution. - + Returns: A random index based on the weights. - + Raises: ValueError: If weights is empty or contains negative values. )") @@ -246,10 +246,10 @@ random numbers following different distributions. Args: trials: The number of trials. probabilities: The probabilities of each outcome. - + Returns: A vector of counts for each outcome. - + Raises: ValueError: If probabilities is invalid. )") @@ -277,7 +277,7 @@ random numbers following different distributions. Args: data: The list of data to shuffle. - + Returns: A new shuffled list. )") @@ -311,10 +311,10 @@ random numbers following different distributions. Args: data: The list of data to sample from. sample_size: The number of elements to sample. - + Returns: A list containing the sampled elements. - + Raises: ValueError: If sample_size > len(data). )"); @@ -326,4 +326,4 @@ random numbers following different distributions. m.attr("LCG").attr("random_int") = m.attr("LCG").attr("next_int"); m.attr("LCG").attr("randint") = m.attr("LCG").attr("next_int"); m.attr("LCG").attr("choice") = m.attr("LCG").attr("next_discrete"); -} \ No newline at end of file +} diff --git a/python/utils/linq.cpp b/python/utils/linq.cpp index 0d1b7094..666d2878 100644 --- a/python/utils/linq.cpp +++ b/python/utils/linq.cpp @@ -974,4 +974,4 @@ PYBIND11_MODULE(linq, m) { >>> from atom.utils import flatten >>> flatten([[1, 2], [3, 4], [5, 6]]) # [1, 2, 3, 4, 5, 6] )"); -} \ No newline at end of file +} diff --git a/python/utils/qdatetime.cpp b/python/utils/qdatetime.cpp index 4cd98f78..a6d993ad 100644 --- a/python/utils/qdatetime.cpp +++ b/python/utils/qdatetime.cpp @@ -164,4 +164,4 @@ arithmetic operations, and timezone conversions. "set_time", "set_time_zone", "time_zone", "to_local_time", "to_string", "to_time_t", "to_utc"}; }); -} \ No newline at end of file +} diff --git a/python/utils/qprocess.cpp b/python/utils/qprocess.cpp index 4232a18f..0942763d 100644 --- a/python/utils/qprocess.cpp +++ b/python/utils/qprocess.cpp @@ -73,7 +73,7 @@ PYBIND11_MODULE(process, m) { m, "Process", R"(A class to manage and interact with external processes. -This class provides methods to start and control external processes. +This class provides methods to start and control external processes. It allows setting working directories, managing environment variables, and reading from or writing to the process's standard output and error streams. @@ -129,7 +129,7 @@ and reading from or writing to the process's standard output and error streams. Returns: bool: True if the process was started successfully, False otherwise. -In detached mode, the process will run independently of the parent process +In detached mode, the process will run independently of the parent process and will not be terminated when the parent process exits. )") .def( @@ -235,4 +235,4 @@ and will not be terminated when the parent process exits. } return false; // Don't suppress exceptions }); -} \ No newline at end of file +} diff --git a/python/utils/qtimer.cpp b/python/utils/qtimer.cpp index 2e71d08b..40dcaad0 100644 --- a/python/utils/qtimer.cpp +++ b/python/utils/qtimer.cpp @@ -123,7 +123,7 @@ This class provides functionality to measure elapsed time in various units py::class_>( m, "Timer", R"(Modern C++ timer class inspired by Qt's QTimer. - + This class provides timer functionality with callbacks, single-shot mode, and customizable precision. @@ -171,7 +171,7 @@ and customizable precision. py::arg("milliseconds"), py::arg("callback"), py::arg("mode") = atom::utils::Timer::PrecisionMode::PRECISE, R"(Creates a single-shot timer that calls the provided callback after the specified interval. - + Args: milliseconds: Interval in milliseconds callback: Function to call when timer expires @@ -186,4 +186,4 @@ and customizable precision. ... print("Single shot timer fired!") >>> timer = Timer.single_shot(1000, callback) )"); -} \ No newline at end of file +} diff --git a/python/utils/qtimezone.cpp b/python/utils/qtimezone.cpp index 26907742..c790acbb 100644 --- a/python/utils/qtimezone.cpp +++ b/python/utils/qtimezone.cpp @@ -129,4 +129,4 @@ about daylight saving time. Raises: RuntimeError: If the time conversion fails. )"); -} \ No newline at end of file +} diff --git a/python/utils/stopwatcher.cpp b/python/utils/stopwatcher.cpp index 0f0a0170..b0133ff1 100644 --- a/python/utils/stopwatcher.cpp +++ b/python/utils/stopwatcher.cpp @@ -254,10 +254,10 @@ When exiting a context started with 'with StopWatcher() as sw:', the stopwatch s Args: function: Function to execute and time. - + Returns: tuple: A tuple containing (function_result, elapsed_time_ms). - + Examples: >>> from atom.utils import timed_execution >>> def my_func(): @@ -289,12 +289,12 @@ When exiting a context started with 'with StopWatcher() as sw:', the stopwatch s Args: milliseconds: Time in milliseconds. - + Returns: str: Formatted time string. - + Examples: >>> from atom.utils import format_time >>> formatted = format_time(65432) # "00:01:05.432" )"); -} \ No newline at end of file +} diff --git a/python/utils/time.cpp b/python/utils/time.cpp index 89c755c4..e23c57f6 100644 --- a/python/utils/time.cpp +++ b/python/utils/time.cpp @@ -58,7 +58,7 @@ pattern "%Y-%m-%d %H:%M:%S". Returns: str: The current timestamp formatted as "%Y-%m-%d %H:%M:%S" - + Raises: TimeConvertException: If time conversion fails @@ -81,7 +81,7 @@ the same format. Returns: str: The corresponding time in China Standard Time, formatted as "%Y-%m-%d %H:%M:%S" - + Raises: TimeConvertException: If the input format is invalid or conversion fails @@ -100,7 +100,7 @@ formatted as a string with the pattern "%Y-%m-%d %H:%M:%S". Returns: str: The current China Standard Time formatted as "%Y-%m-%d %H:%M:%S" - + Raises: TimeConvertException: If time conversion fails @@ -123,7 +123,7 @@ converts it to a string representation. Returns: str: The string representation of the timestamp - + Raises: TimeConvertException: If the timestamp is invalid or conversion fails @@ -152,7 +152,7 @@ converts it to a formatted string according to the specified format. Returns: str: The formatted time string based on the tm structure and format - + Raises: TimeConvertException: If formatting fails @@ -172,7 +172,7 @@ pattern "%Y-%m-%d %H:%M:%S". Returns: str: The current UTC time formatted as "%Y-%m-%d %H:%M:%S" - + Raises: TimeConvertException: If time conversion fails @@ -193,7 +193,7 @@ converts it to a tm structure, which represents a calendar date and time. timestamp: The timestamp to be converted, in seconds since the Unix epoch Returns: - Optional[tm]: The corresponding tm structure representing the timestamp, + Optional[tm]: The corresponding tm structure representing the timestamp, or None if conversion fails Examples: @@ -231,7 +231,7 @@ converts it to a tm structure, which represents a calendar date and time. Returns: int: Elapsed time in milliseconds - + Raises: TypeError: If the input is not a valid time point @@ -319,7 +319,7 @@ converts it to a tm structure, which represents a calendar date and time. Returns: tm: The parsed time as a tm structure - + Raises: ValueError: If parsing fails @@ -369,7 +369,7 @@ converts it to a tm structure, which represents a calendar date and time. Returns: float: The difference in seconds (time2 - time1) - + Raises: ValueError: If parsing or conversion fails @@ -379,4 +379,4 @@ converts it to a tm structure, which represents a calendar date and time. >>> diff 30.0 )"); -} \ No newline at end of file +} diff --git a/python/utils/uuid.cpp b/python/utils/uuid.cpp index aaf78dc4..d8086fa9 100644 --- a/python/utils/uuid.cpp +++ b/python/utils/uuid.cpp @@ -444,4 +444,4 @@ when available on the platform. m.attr("NAMESPACE_X500") = atom::utils::UUID::fromString("6ba7b814-9dad-11d1-80b4-00c04fd430c8") .value(); -} \ No newline at end of file +} diff --git a/python/web/__init__.py b/python/web/__init__.py index cb4210bd..4a1b1238 100644 --- a/python/web/__init__.py +++ b/python/web/__init__.py @@ -1 +1 @@ -# Auto-generated __init__.py for web module \ No newline at end of file +# Auto-generated __init__.py for web module diff --git a/python/web/address.cpp b/python/web/address.cpp index b95fd1af..029ac2f3 100644 --- a/python/web/address.cpp +++ b/python/web/address.cpp @@ -339,4 +339,4 @@ and path validation. >>> is_valid_address("not-an-address") False )"); -} \ No newline at end of file +} diff --git a/python/web/downloader.cpp b/python/web/downloader.cpp index 898ba7af..f279673f 100644 --- a/python/web/downloader.cpp +++ b/python/web/downloader.cpp @@ -259,4 +259,4 @@ It supports multi-threaded downloads, download speed control, and progress callb >>> download_files(files, 4) # Download with 4 threads 2 )"); -} \ No newline at end of file +} diff --git a/python/web/httpparser.cpp b/python/web/httpparser.cpp index d7f0638b..e77a25a5 100644 --- a/python/web/httpparser.cpp +++ b/python/web/httpparser.cpp @@ -551,7 +551,7 @@ requests, and responses. Examples: >>> from atom.web.httpparser import create_request, HttpMethod, HttpVersion - >>> parser = create_request(HttpMethod.POST, "/api/data", HttpVersion.HTTP_1_1, + >>> parser = create_request(HttpMethod.POST, "/api/data", HttpVersion.HTTP_1_1, ... {"Content-Type": ["application/json"]}, '{"key": "value"}') >>> parser.build_request() 'POST /api/data HTTP/1.1\r\nContent-Type: application/json\r\n\r\n{"key": "value"}' @@ -587,9 +587,9 @@ requests, and responses. Examples: >>> from atom.web.httpparser import create_response, HttpStatus, HttpVersion - >>> parser = create_response(HttpStatus.OK(), HttpVersion.HTTP_1_1, + >>> parser = create_response(HttpStatus.OK(), HttpVersion.HTTP_1_1, ... {"Content-Type": ["text/html"]}, 'Hello') >>> parser.build_response() 'HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\nHello' )"); -} \ No newline at end of file +} diff --git a/python/web/mimetype.cpp b/python/web/mimetype.cpp index 1fae971f..62702aec 100644 --- a/python/web/mimetype.cpp +++ b/python/web/mimetype.cpp @@ -296,4 +296,4 @@ or when you plan to load data later. >>> print(mime_type) text/plain )"); -} \ No newline at end of file +} diff --git a/python/web/utils.cpp b/python/web/utils.cpp index 58f38ff2..e517667f 100644 --- a/python/web/utils.cpp +++ b/python/web/utils.cpp @@ -440,4 +440,4 @@ checking if a host is reachable. >>> hostname_to_ip("example.com") ['93.184.216.34', '2606:2800:220:1:248:1893:25c8:1946'] )"); -} \ No newline at end of file +} diff --git a/scripts/setup_vcpkg.bat b/scripts/setup_vcpkg.bat index 00ac6641..beb64a57 100644 --- a/scripts/setup_vcpkg.bat +++ b/scripts/setup_vcpkg.bat @@ -48,14 +48,14 @@ echo %YELLOW%vcpkg not found. Do you want to install it? (Y/N)%RESET% set /p INSTALL_CHOICE="> " if /i "%INSTALL_CHOICE%"=="Y" ( echo %GREEN%Installing vcpkg...%RESET% - + REM Determine installation path echo %YELLOW%Please select vcpkg installation location:%RESET% echo 1. User home directory (%USERPROFILE%\vcpkg) echo 2. C drive root (C:\vcpkg) echo 3. Current directory (%cd%\vcpkg) set /p INSTALL_LOCATION="> " - + if "%INSTALL_LOCATION%"=="1" ( set "VCPKG_PATH=%USERPROFILE%\vcpkg" ) else if "%INSTALL_LOCATION%"=="2" ( @@ -66,28 +66,28 @@ if /i "%INSTALL_CHOICE%"=="Y" ( echo %RED%Invalid choice. Using default location (%USERPROFILE%\vcpkg)%RESET% set "VCPKG_PATH=%USERPROFILE%\vcpkg" ) - + REM Clone and bootstrap vcpkg if exist "%VCPKG_PATH%" ( echo %YELLOW%Directory %VCPKG_PATH% already exists. Continue? (Y/N)%RESET% set /p CONTINUE_CHOICE="> " if /i not "%CONTINUE_CHOICE%"=="Y" goto :eof ) - + echo %GREEN%Cloning vcpkg to %VCPKG_PATH%...%RESET% git clone https://github.com/microsoft/vcpkg.git "%VCPKG_PATH%" if %ERRORLEVEL% neq 0 ( echo %RED%Failed to clone vcpkg%RESET% goto :eof ) - + echo %GREEN%Bootstrapping vcpkg...%RESET% call "%VCPKG_PATH%\bootstrap-vcpkg.bat" -disableMetrics if %ERRORLEVEL% neq 0 ( echo %RED%Failed to bootstrap vcpkg%RESET% goto :eof ) - + REM Set VCPKG_ROOT environment variable echo %GREEN%Setting VCPKG_ROOT environment variable...%RESET% setx VCPKG_ROOT "%VCPKG_PATH%" @@ -123,18 +123,18 @@ set "TRIPLET=%ARCH%-windows" if %IS_MSYS2% equ 1 ( set "TRIPLET=%ARCH%-mingw-dynamic" echo %GREEN%MSYS2: Using triplet %TRIPLET%%RESET% - + REM Check if MinGW triplet needs to be created if not exist "%VCPKG_PATH%\triplets\community\%TRIPLET%.cmake" ( echo %YELLOW%Need to create MinGW triplet file: %TRIPLET%%RESET% - + mkdir "%VCPKG_PATH%\triplets\community" 2>nul - + echo set(VCPKG_TARGET_ARCHITECTURE %ARCH%) > "%VCPKG_PATH%\triplets\community\%TRIPLET%.cmake" echo set(VCPKG_CRT_LINKAGE dynamic) >> "%VCPKG_PATH%\triplets\community\%TRIPLET%.cmake" echo set(VCPKG_LIBRARY_LINKAGE dynamic) >> "%VCPKG_PATH%\triplets\community\%TRIPLET%.cmake" echo set(VCPKG_CMAKE_SYSTEM_NAME MinGW) >> "%VCPKG_PATH%\triplets\community\%TRIPLET%.cmake" - + echo %GREEN%Triplet file created: %TRIPLET%%RESET% fi ) @@ -158,7 +158,7 @@ if /i "%OPTIONAL_DEPS%"=="Y" ( if %ERRORLEVEL% neq 0 ( echo %YELLOW%Warning: Failed to install optional Boost components%RESET% ) - + echo %GREEN%Installing test components...%RESET% "%VCPKG_PATH%\vcpkg.exe" install gtest --triplet=%TRIPLET% if %ERRORLEVEL% neq 0 ( @@ -192,7 +192,7 @@ if /i "%CONFIG_NOW%"=="Y" ( echo %RED%Project configuration failed%RESET% ) else { echo %GREEN%Project configured successfully!%RESET% - + echo %YELLOW%Start build now? (Y/N)%RESET% set /p BUILD_NOW="> " if /i "%BUILD_NOW%"=="Y" ( @@ -207,4 +207,4 @@ if /i "%CONFIG_NOW%"=="Y" ( } ) -pause \ No newline at end of file +pause diff --git a/scripts/setup_vcpkg.ps1 b/scripts/setup_vcpkg.ps1 index c88018eb..c54bce9d 100644 --- a/scripts/setup_vcpkg.ps1 +++ b/scripts/setup_vcpkg.ps1 @@ -34,17 +34,17 @@ else { # vcpkg not found, prompt to install Write-Yellow "vcpkg not found. Do you want to install it? (Y/N)" $installChoice = Read-Host "> " - + if ($installChoice -eq "Y" -or $installChoice -eq "y") { Write-Green "Installing vcpkg..." - + # Determine installation path Write-Yellow "Please select vcpkg installation location:" Write-Host "1. User home directory ($env:USERPROFILE\vcpkg)" Write-Host "2. C drive root (C:\vcpkg)" Write-Host "3. Current directory ($(Get-Location)\vcpkg)" $installLocation = Read-Host "> " - + switch ($installLocation) { "1" { $VcpkgPath = "$env:USERPROFILE\vcpkg" } "2" { $VcpkgPath = "C:\vcpkg" } @@ -54,7 +54,7 @@ else { $VcpkgPath = "$env:USERPROFILE\vcpkg" } } - + # Clone and bootstrap vcpkg if (Test-Path $VcpkgPath) { Write-Yellow "Directory $VcpkgPath already exists. Continue? (Y/N)" @@ -63,21 +63,21 @@ else { exit } } - + Write-Green "Cloning vcpkg to $VcpkgPath..." git clone https://github.com/microsoft/vcpkg.git $VcpkgPath if ($LASTEXITCODE -ne 0) { Write-Red "Failed to clone vcpkg" exit } - + Write-Green "Bootstrapping vcpkg..." & "$VcpkgPath\bootstrap-vcpkg.bat" -disableMetrics if ($LASTEXITCODE -ne 0) { Write-Red "Failed to bootstrap vcpkg" exit } - + # Set VCPKG_ROOT environment variable Write-Green "Setting VCPKG_ROOT environment variable..." try { @@ -114,18 +114,18 @@ $Triplet = "$Arch-windows" if ($IsMsys2) { $Triplet = "$Arch-mingw-dynamic" Write-Green "MSYS2: Using triplet $Triplet" - + # Check if MinGW triplet needs to be created $TripletFile = "$VcpkgPath\triplets\community\$Triplet.cmake" if (-not (Test-Path $TripletFile)) { Write-Yellow "Need to create MinGW triplet file: $Triplet" - + # Create directory if it doesn't exist $TripletDir = "$VcpkgPath\triplets\community" if (-not (Test-Path $TripletDir)) { New-Item -Path $TripletDir -ItemType Directory -Force | Out-Null } - + # Create the triplet file @" set(VCPKG_TARGET_ARCHITECTURE $Arch) @@ -133,7 +133,7 @@ set(VCPKG_CRT_LINKAGE dynamic) set(VCPKG_LIBRARY_LINKAGE dynamic) set(VCPKG_CMAKE_SYSTEM_NAME MinGW) "@ | Set-Content -Path $TripletFile - + Write-Green "Triplet file created: $Triplet" } } @@ -157,7 +157,7 @@ if ($optionalDeps -eq "Y" -or $optionalDeps -eq "y") { if ($LASTEXITCODE -ne 0) { Write-Yellow "Warning: Failed to install optional Boost components" } - + Write-Green "Installing test components..." & "$VcpkgPath\vcpkg.exe" install gtest --triplet=$Triplet if ($LASTEXITCODE -ne 0) { @@ -187,19 +187,19 @@ $configNow = Read-Host "> " if ($configNow -eq "Y" -or $configNow -eq "y") { Write-Green "Configuring project..." & cmake -B build -G "Ninja" -DCMAKE_TOOLCHAIN_FILE="$VcpkgPath/scripts/buildsystems/vcpkg.cmake" -DVCPKG_TARGET_TRIPLET=$Triplet - + if ($LASTEXITCODE -ne 0) { Write-Red "Project configuration failed" } else { Write-Green "Project configured successfully!" - + Write-Yellow "Start build now? (Y/N)" $buildNow = Read-Host "> " if ($buildNow -eq "Y" -or $buildNow -eq "y") { Write-Green "Building project..." & cmake --build build - + if ($LASTEXITCODE -ne 0) { Write-Red "Project build failed" } @@ -211,4 +211,4 @@ if ($configNow -eq "Y" -or $configNow -eq "y") { } Write-Host "Press any key to continue..." -$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") \ No newline at end of file +$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") diff --git a/tests/algorithm/test_blowfish.cpp b/tests/algorithm/test_blowfish.cpp index 48ed2c63..dcfb9973 100644 --- a/tests/algorithm/test_blowfish.cpp +++ b/tests/algorithm/test_blowfish.cpp @@ -492,4 +492,4 @@ int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); spdlog::set_level(spdlog::level::off); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/algorithm/test_fraction.cpp b/tests/algorithm/test_fraction.cpp index 16284e21..9b8d8071 100644 --- a/tests/algorithm/test_fraction.cpp +++ b/tests/algorithm/test_fraction.cpp @@ -440,4 +440,4 @@ TEST_F(FractionTest, ChainedOperations) { // 1/2 + 1/3 = 5/6 // 5/6 - 1/20 = 100/120 - 6/120 = 94/120 = 47/60 EXPECT_EQ(result.toString(), "47/60"); -} \ No newline at end of file +} diff --git a/tests/algorithm/test_math.cpp b/tests/algorithm/test_math.cpp index 8612d52d..0b1282fb 100644 --- a/tests/algorithm/test_math.cpp +++ b/tests/algorithm/test_math.cpp @@ -208,4 +208,4 @@ TEST(MathTest, ModPow) { int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/algorithm/test_mhash.cpp b/tests/algorithm/test_mhash.cpp index a9d7b369..1ece3d3f 100644 --- a/tests/algorithm/test_mhash.cpp +++ b/tests/algorithm/test_mhash.cpp @@ -270,4 +270,4 @@ TEST_F(MHashTest, ThreadSafety) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/algorithm/test_sha1.cpp b/tests/algorithm/test_sha1.cpp index 960604d6..4b99898b 100644 --- a/tests/algorithm/test_sha1.cpp +++ b/tests/algorithm/test_sha1.cpp @@ -455,4 +455,4 @@ TEST_F(SHA1Test, BinaryData) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/algorithm/test_tea.cpp b/tests/algorithm/test_tea.cpp index b3986e04..91ae8339 100644 --- a/tests/algorithm/test_tea.cpp +++ b/tests/algorithm/test_tea.cpp @@ -486,4 +486,4 @@ TEST_F(TEATest, RandomData) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/extra/beast/test_http.cpp b/tests/extra/beast/test_http.cpp index 70be67a0..89899be6 100644 --- a/tests/extra/beast/test_http.cpp +++ b/tests/extra/beast/test_http.cpp @@ -323,4 +323,4 @@ TEST_F(HttpClientTest, InvalidValues) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/extra/beast/test_ws.cpp b/tests/extra/beast/test_ws.cpp index ba9827c4..b9b2e8d7 100644 --- a/tests/extra/beast/test_ws.cpp +++ b/tests/extra/beast/test_ws.cpp @@ -481,4 +481,4 @@ TEST_F(WSClientTest, DestructorBehavior) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/extra/boost/test_charconv.hpp b/tests/extra/boost/test_charconv.hpp index 439213f9..720aaa26 100644 --- a/tests/extra/boost/test_charconv.hpp +++ b/tests/extra/boost/test_charconv.hpp @@ -310,4 +310,4 @@ TEST_F(BoostCharConvTest, ExtremeValues) { } // namespace atom::extra::boost::test -#endif // ATOM_EXTRA_BOOST_TEST_CHARCONV_HPP \ No newline at end of file +#endif // ATOM_EXTRA_BOOST_TEST_CHARCONV_HPP diff --git a/tests/extra/boost/test_locale.hpp b/tests/extra/boost/test_locale.hpp index 18e6fc3b..3a195bcf 100644 --- a/tests/extra/boost/test_locale.hpp +++ b/tests/extra/boost/test_locale.hpp @@ -541,4 +541,4 @@ TEST_F(LocaleWrapperTest, EdgeCases) { } // namespace atom::extra::boost::test -#endif // ATOM_EXTRA_BOOST_TEST_LOCALE_HPP \ No newline at end of file +#endif // ATOM_EXTRA_BOOST_TEST_LOCALE_HPP diff --git a/tests/extra/boost/test_math.hpp b/tests/extra/boost/test_math.hpp index 05125d52..7c6017cc 100644 --- a/tests/extra/boost/test_math.hpp +++ b/tests/extra/boost/test_math.hpp @@ -638,4 +638,4 @@ TEST_F(FinancialMathTest, ImpliedVolatility) { } // namespace atom::extra::boost::test -#endif // ATOM_EXTRA_BOOST_TEST_MATH_HPP \ No newline at end of file +#endif // ATOM_EXTRA_BOOST_TEST_MATH_HPP diff --git a/tests/extra/boost/test_regex.hpp b/tests/extra/boost/test_regex.hpp index 1cef74c5..b54cac62 100644 --- a/tests/extra/boost/test_regex.hpp +++ b/tests/extra/boost/test_regex.hpp @@ -549,4 +549,4 @@ TEST_F(RegexWrapperTest, EdgeCases) { } // namespace atom::extra::boost::test -#endif // ATOM_EXTRA_BOOST_TEST_REGEX_HPP \ No newline at end of file +#endif // ATOM_EXTRA_BOOST_TEST_REGEX_HPP diff --git a/tests/extra/boost/test_system.hpp b/tests/extra/boost/test_system.hpp index 407e0983..2815defb 100644 --- a/tests/extra/boost/test_system.hpp +++ b/tests/extra/boost/test_system.hpp @@ -593,4 +593,4 @@ TEST_F(IntegrationTest, ResultMapping) { } // namespace atom::extra::boost::test -#endif // ATOM_EXTRA_BOOST_TEST_SYSTEM_HPP \ No newline at end of file +#endif // ATOM_EXTRA_BOOST_TEST_SYSTEM_HPP diff --git a/tests/extra/boost/test_uuid.hpp b/tests/extra/boost/test_uuid.hpp index cbce88b3..f59c4cfe 100644 --- a/tests/extra/boost/test_uuid.hpp +++ b/tests/extra/boost/test_uuid.hpp @@ -35,17 +35,17 @@ class UUIDTest : public ::testing::Test { void SetUp() override { // Create a nil UUID nilUUID = std::make_unique(::boost::uuids::nil_uuid()); - + // Create a UUID from a fixed string for consistent testing const std::string testUUIDString = "123e4567-e89b-12d3-a456-426614174000"; fixedUUID = std::make_unique(testUUIDString); - + // Static predefined namespace UUIDs dnsNamespaceUUID = std::make_unique(UUID::namespaceDNS()); urlNamespaceUUID = std::make_unique(UUID::namespaceURL()); oidNamespaceUUID = std::make_unique(UUID::namespaceOID()); } - + void TearDown() override { nilUUID.reset(); fixedUUID.reset(); @@ -53,22 +53,22 @@ class UUIDTest : public ::testing::Test { urlNamespaceUUID.reset(); oidNamespaceUUID.reset(); } - + // Helper functions for testing static bool isValidUUIDString(const std::string& str) { std::regex uuidRegex( - "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$", + "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$", std::regex::icase ); return std::regex_match(str, uuidRegex); } - + static bool isValidBase64String(const std::string& str) { // Base64 consists of alphanumeric chars, '+', and '/' std::regex base64Regex("^[A-Za-z0-9+/]+={0,2}$"); return std::regex_match(str, base64Regex); } - + std::unique_ptr nilUUID; std::unique_ptr fixedUUID; std::unique_ptr dnsNamespaceUUID; @@ -82,17 +82,17 @@ TEST_F(UUIDTest, Constructors) { UUID randomUUID; EXPECT_FALSE(randomUUID.isNil()); EXPECT_TRUE(isValidUUIDString(randomUUID.toString())); - + // Test constructor with string std::string uuidStr = "123e4567-e89b-12d3-a456-426614174000"; UUID fromString(uuidStr); EXPECT_EQ(fromString.toString(), uuidStr); - + // Test constructor with Boost UUID ::boost::uuids::uuid boostUUID = ::boost::uuids::nil_uuid(); UUID fromBoostUUID(boostUUID); EXPECT_TRUE(fromBoostUUID.isNil()); - + // Test constructor with invalid string (should throw) EXPECT_THROW(UUID("not-a-uuid"), std::runtime_error); } @@ -103,10 +103,10 @@ TEST_F(UUIDTest, ToString) { std::string nilString = nilUUID->toString(); EXPECT_TRUE(isValidUUIDString(nilString)); EXPECT_EQ(nilString, "00000000-0000-0000-0000-000000000000"); - + // Fixed UUID should match its string representation EXPECT_EQ(fixedUUID->toString(), "123e4567-e89b-12d3-a456-426614174000"); - + // Random UUID should have a valid string representation UUID randomUUID; EXPECT_TRUE(isValidUUIDString(randomUUID.toString())); @@ -116,10 +116,10 @@ TEST_F(UUIDTest, ToString) { TEST_F(UUIDTest, IsNil) { // Nil UUID should be nil EXPECT_TRUE(nilUUID->isNil()); - + // Fixed UUID should not be nil EXPECT_FALSE(fixedUUID->isNil()); - + // Random UUID should not be nil UUID randomUUID; EXPECT_FALSE(randomUUID.isNil()); @@ -130,21 +130,21 @@ TEST_F(UUIDTest, ComparisonOperators) { // Create copies of UUIDs UUID nilCopy(*nilUUID); UUID fixedCopy(*fixedUUID); - + // Test equality EXPECT_TRUE(*nilUUID == nilCopy); EXPECT_TRUE(*fixedUUID == fixedCopy); EXPECT_FALSE(*nilUUID == *fixedUUID); - + // Test inequality EXPECT_FALSE(*nilUUID != nilCopy); EXPECT_FALSE(*fixedUUID != fixedCopy); EXPECT_TRUE(*nilUUID != *fixedUUID); - + // Test spaceship operator EXPECT_TRUE((*nilUUID <=> nilCopy) == std::strong_ordering::equal); EXPECT_TRUE((*fixedUUID <=> fixedCopy) == std::strong_ordering::equal); - + // The actual comparison depends on the underlying bytes, so we can't easily // predict less/greater, but we can check it's consistent auto compResult = *nilUUID <=> *fixedUUID; @@ -160,11 +160,11 @@ TEST_F(UUIDTest, Format) { // Nil UUID format std::string nilFormat = nilUUID->format(); EXPECT_EQ(nilFormat, "{00000000-0000-0000-0000-000000000000}"); - + // Fixed UUID format std::string fixedFormat = fixedUUID->format(); EXPECT_EQ(fixedFormat, "{123e4567-e89b-12d3-a456-426614174000}"); - + // Random UUID format UUID randomUUID; std::string randomFormat = randomUUID.format(); @@ -179,22 +179,22 @@ TEST_F(UUIDTest, ByteConversion) { std::vector nilBytes = nilUUID->toBytes(); EXPECT_EQ(nilBytes.size(), atom::extra::boost::UUID_SIZE); EXPECT_TRUE(std::all_of(nilBytes.begin(), nilBytes.end(), [](uint8_t b) { return b == 0; })); - + std::vector fixedBytes = fixedUUID->toBytes(); EXPECT_EQ(fixedBytes.size(), atom::extra::boost::UUID_SIZE); - + // Test fromBytes with valid input UUID reconstructedNil = UUID::fromBytes(std::span(nilBytes)); EXPECT_TRUE(reconstructedNil.isNil()); EXPECT_EQ(reconstructedNil, *nilUUID); - + UUID reconstructedFixed = UUID::fromBytes(std::span(fixedBytes)); EXPECT_EQ(reconstructedFixed, *fixedUUID); - + // Test fromBytes with invalid input std::vector tooShort(15, 0); EXPECT_THROW(UUID::fromBytes(std::span(tooShort)), std::invalid_argument); - + std::vector tooLong(17, 0); EXPECT_THROW(UUID::fromBytes(std::span(tooLong)), std::invalid_argument); } @@ -203,11 +203,11 @@ TEST_F(UUIDTest, ByteConversion) { TEST_F(UUIDTest, ToUint64) { // Nil UUID should convert to 0 EXPECT_EQ(nilUUID->toUint64(), 0); - + // Fixed UUID conversion should be deterministic uint64_t fixedValue = fixedUUID->toUint64(); EXPECT_NE(fixedValue, 0); - + // Creating a new UUID with the same string should give the same uint64 UUID fixedCopy("123e4567-e89b-12d3-a456-426614174000"); EXPECT_EQ(fixedCopy.toUint64(), fixedValue); @@ -218,11 +218,11 @@ TEST_F(UUIDTest, NamespaceUUIDs) { // Test DNS namespace UUID EXPECT_FALSE(dnsNamespaceUUID->isNil()); EXPECT_EQ(dnsNamespaceUUID->toString(), "6ba7b810-9dad-11d1-80b4-00c04fd430c8"); - + // Test URL namespace UUID EXPECT_FALSE(urlNamespaceUUID->isNil()); EXPECT_EQ(urlNamespaceUUID->toString(), "6ba7b811-9dad-11d1-80b4-00c04fd430c8"); - + // Test OID namespace UUID EXPECT_FALSE(oidNamespaceUUID->isNil()); EXPECT_EQ(oidNamespaceUUID->toString(), "6ba7b812-9dad-11d1-80b4-00c04fd430c8"); @@ -233,16 +233,16 @@ TEST_F(UUIDTest, V3UUID) { // Generate v3 UUIDs with the same namespace and name UUID v3_1 = UUID::v3(*dnsNamespaceUUID, "example.com"); UUID v3_2 = UUID::v3(*dnsNamespaceUUID, "example.com"); - + // They should be the same EXPECT_EQ(v3_1, v3_2); EXPECT_EQ(v3_1.version(), 3); - + // Generate v3 UUIDs with different names UUID v3_3 = UUID::v3(*dnsNamespaceUUID, "example.org"); EXPECT_NE(v3_1, v3_3); EXPECT_EQ(v3_3.version(), 3); - + // Generate v3 UUIDs with different namespaces UUID v3_4 = UUID::v3(*urlNamespaceUUID, "example.com"); EXPECT_NE(v3_1, v3_4); @@ -254,21 +254,21 @@ TEST_F(UUIDTest, V5UUID) { // Generate v5 UUIDs with the same namespace and name UUID v5_1 = UUID::v5(*dnsNamespaceUUID, "example.com"); UUID v5_2 = UUID::v5(*dnsNamespaceUUID, "example.com"); - + // They should be the same EXPECT_EQ(v5_1, v5_2); EXPECT_EQ(v5_1.version(), 5); - + // Generate v5 UUIDs with different names UUID v5_3 = UUID::v5(*dnsNamespaceUUID, "example.org"); EXPECT_NE(v5_1, v5_3); EXPECT_EQ(v5_3.version(), 5); - + // Generate v5 UUIDs with different namespaces UUID v5_4 = UUID::v5(*urlNamespaceUUID, "example.com"); EXPECT_NE(v5_1, v5_4); EXPECT_EQ(v5_4.version(), 5); - + // v3 and v5 UUIDs for the same name should be different UUID v3 = UUID::v3(*dnsNamespaceUUID, "example.com"); UUID v5 = UUID::v5(*dnsNamespaceUUID, "example.com"); @@ -279,31 +279,31 @@ TEST_F(UUIDTest, V5UUID) { TEST_F(UUIDTest, VersionAndVariant) { // Nil UUID should have version 0 EXPECT_EQ(nilUUID->version(), 0); - + // Random UUID (v4) should have version 4 UUID v4UUID = UUID::v4(); EXPECT_EQ(v4UUID.version(), 4); - + // v3 UUID should have version 3 UUID v3UUID = UUID::v3(*dnsNamespaceUUID, "example.com"); EXPECT_EQ(v3UUID.version(), 3); - + // v5 UUID should have version 5 UUID v5UUID = UUID::v5(*dnsNamespaceUUID, "example.com"); EXPECT_EQ(v5UUID.version(), 5); - + // v1 UUID should have version 1 UUID v1UUID = UUID::v1(); // Note: Test this if v1() actually generates v1 UUIDs if (v1UUID.version() == 1) { EXPECT_EQ(v1UUID.version(), 1); } - + // Variant should be correct for all UUIDs (DCE 1.1 variant) EXPECT_EQ(v4UUID.variant(), 1); EXPECT_EQ(v3UUID.variant(), 1); EXPECT_EQ(v5UUID.variant(), 1); - + // Nil UUID variant might be 0 // This is implementation-defined, so we don't make strict assertions } @@ -313,19 +313,19 @@ TEST_F(UUIDTest, V1AndV4UUID) { // Generate multiple v1 UUIDs UUID v1_1 = UUID::v1(); UUID v1_2 = UUID::v1(); - + // They should be different EXPECT_NE(v1_1, v1_2); - + // Generate multiple v4 UUIDs UUID v4_1 = UUID::v4(); UUID v4_2 = UUID::v4(); - + // They should be different EXPECT_NE(v4_1, v4_2); EXPECT_EQ(v4_1.version(), 4); EXPECT_EQ(v4_2.version(), 4); - + // v1 and v4 UUIDs should be different EXPECT_NE(v1_1, v4_1); } @@ -335,21 +335,21 @@ TEST_F(UUIDTest, ToBase64) { // Test nil UUID base64 std::string nilBase64 = nilUUID->toBase64(); EXPECT_EQ(nilBase64.size(), atom::extra::boost::BASE64_RESERVE_SIZE); - + // Test fixed UUID base64 std::string fixedBase64 = fixedUUID->toBase64(); EXPECT_EQ(fixedBase64.size(), atom::extra::boost::BASE64_RESERVE_SIZE); EXPECT_TRUE(isValidBase64String(fixedBase64)); - + // Random UUID base64 UUID randomUUID; std::string randomBase64 = randomUUID.toBase64(); EXPECT_EQ(randomBase64.size(), atom::extra::boost::BASE64_RESERVE_SIZE); EXPECT_TRUE(isValidBase64String(randomBase64)); - + // Converting the same UUID twice should give the same base64 EXPECT_EQ(fixedUUID->toBase64(), fixedBase64); - + // Different UUIDs should give different base64 strings EXPECT_NE(nilUUID->toBase64(), fixedUUID->toBase64()); } @@ -358,28 +358,28 @@ TEST_F(UUIDTest, ToBase64) { TEST_F(UUIDTest, GetTimestamp) { // Create a v1 UUID UUID v1UUID = UUID::v1(); - + // If it's actually a v1 UUID, test getTimestamp if (v1UUID.version() == 1) { // Getting timestamp should not throw for v1 UUID EXPECT_NO_THROW({ auto timestamp = v1UUID.getTimestamp(); }); - + // Timestamp should be recent auto timestamp = v1UUID.getTimestamp(); auto now = std::chrono::system_clock::now(); - + // It should be within a reasonable time range from now // Note: This is approximate and may fail if time zones are involved auto timeDiff = std::chrono::duration_cast(now - timestamp).count(); EXPECT_LE(std::abs(timeDiff), 366); // Within a year (generous margin) } - + // Getting timestamp from non-v1 UUID should throw UUID v4UUID = UUID::v4(); EXPECT_THROW(v4UUID.getTimestamp(), std::runtime_error); - + EXPECT_THROW(nilUUID->getTimestamp(), std::runtime_error); } @@ -389,32 +389,32 @@ TEST_F(UUIDTest, HashFunction) { UUID u1 = UUID::v4(); UUID u2 = UUID::v4(); UUID u1Copy = UUID(u1.toString()); - + // Create hash function std::hash hasher; - + // Same UUIDs should have same hash EXPECT_EQ(hasher(u1), hasher(u1Copy)); - + // Different UUIDs should (probably) have different hashes // This is not guaranteed but highly likely EXPECT_NE(hasher(u1), hasher(u2)); - + // Test in hash containers std::unordered_set uuidSet; uuidSet.insert(u1); uuidSet.insert(u2); uuidSet.insert(u1Copy); // Should not increase the size since u1 is already there - + EXPECT_EQ(uuidSet.size(), 2); EXPECT_TRUE(uuidSet.contains(u1)); EXPECT_TRUE(uuidSet.contains(u2)); - + std::unordered_map uuidMap; uuidMap[u1] = 1; uuidMap[u2] = 2; uuidMap[u1Copy] = 3; // Should update the value for u1 - + EXPECT_EQ(uuidMap.size(), 2); EXPECT_EQ(uuidMap[u1], 3); EXPECT_EQ(uuidMap[u2], 2); @@ -424,10 +424,10 @@ TEST_F(UUIDTest, HashFunction) { TEST_F(UUIDTest, GetUUID) { // Get the underlying Boost UUID const auto& boostUUID = nilUUID->getUUID(); - + // Verify it's the correct type and value EXPECT_TRUE(boostUUID.is_nil()); - + // Create a new UUID from the Boost UUID UUID newUUID(boostUUID); EXPECT_EQ(newUUID, *nilUUID); @@ -437,14 +437,14 @@ TEST_F(UUIDTest, GetUUID) { TEST_F(UUIDTest, Uniqueness) { constexpr int NUM_UUIDS = 1000; std::set uuidStrings; - + // Generate a bunch of UUIDs and ensure they're all unique for (int i = 0; i < NUM_UUIDS; ++i) { UUID uuid = UUID::v4(); std::string uuidStr = uuid.toString(); EXPECT_TRUE(uuidStrings.insert(uuidStr).second) << "UUID collision detected: " << uuidStr; } - + EXPECT_EQ(uuidStrings.size(), NUM_UUIDS); } @@ -454,17 +454,17 @@ TEST_F(UUIDTest, EdgeCases) { EXPECT_THROW(UUID("not-a-uuid"), std::runtime_error); EXPECT_THROW(UUID("123456789"), std::runtime_error); EXPECT_THROW(UUID("123e4567-e89b-12d3-a456-4266141740"), std::runtime_error); // Too short - + // Empty string for UUID constructor EXPECT_THROW(UUID(""), std::runtime_error); - + // Invalid bytes for fromBytes std::vector tooShort(15, 0); EXPECT_THROW(UUID::fromBytes(std::span(tooShort)), std::invalid_argument); - + std::vector tooLong(17, 0); EXPECT_THROW(UUID::fromBytes(std::span(tooLong)), std::invalid_argument); - + // Empty bytes should throw std::vector empty; EXPECT_THROW(UUID::fromBytes(std::span(empty)), std::invalid_argument); @@ -473,16 +473,16 @@ TEST_F(UUIDTest, EdgeCases) { // Verify that UUIDs can be sorted (for use in ordered containers) TEST_F(UUIDTest, SortingBehavior) { std::vector uuids; - + // Add some UUIDs uuids.push_back(*nilUUID); uuids.push_back(*fixedUUID); uuids.push_back(UUID::v4()); uuids.push_back(UUID::v4()); - + // Should be able to sort without errors EXPECT_NO_THROW(std::sort(uuids.begin(), uuids.end())); - + // Verify the sort is stable (sorting again gives the same result) std::vector uuidsCopy = uuids; std::sort(uuidsCopy.begin(), uuidsCopy.end()); @@ -491,4 +491,4 @@ TEST_F(UUIDTest, SortingBehavior) { } // namespace atom::extra::boost::test -#endif // ATOM_EXTRA_BOOST_TEST_UUID_HPP \ No newline at end of file +#endif // ATOM_EXTRA_BOOST_TEST_UUID_HPP diff --git a/tests/extra/curl/test_rest_client.hpp b/tests/extra/curl/test_rest_client.hpp index 5be75b2c..c547bc98 100644 --- a/tests/extra/curl/test_rest_client.hpp +++ b/tests/extra/curl/test_rest_client.hpp @@ -365,4 +365,4 @@ TEST_F(RestClientTest, ErrorHandlerConcept) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/extra/inicpp/file.cpp b/tests/extra/inicpp/file.cpp index a61534fa..ea51173b 100644 --- a/tests/extra/inicpp/file.cpp +++ b/tests/extra/inicpp/file.cpp @@ -146,4 +146,4 @@ TEST(IniFileBaseTest, Save) { int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/extra/pugixml/test_xml_builder.hpp b/tests/extra/pugixml/test_xml_builder.hpp index ee16f0a1..6ec06bc6 100644 --- a/tests/extra/pugixml/test_xml_builder.hpp +++ b/tests/extra/pugixml/test_xml_builder.hpp @@ -516,4 +516,4 @@ TEST_F(XmlBuilderTest, ChainedOperations) { EXPECT_EQ(second->attribute("id")->value(), "2"); } -} // namespace atom::extra::pugixml::test \ No newline at end of file +} // namespace atom::extra::pugixml::test diff --git a/tests/extra/pugixml/test_xml_document.hpp b/tests/extra/pugixml/test_xml_document.hpp index 6b9cff17..53d38448 100644 --- a/tests/extra/pugixml/test_xml_document.hpp +++ b/tests/extra/pugixml/test_xml_document.hpp @@ -438,4 +438,4 @@ TEST_F(XmlDocumentTest, MixedLoadSaveOptions) { EXPECT_THAT(result, ::testing::HasSubstr("")); } -} // namespace atom::extra::pugixml::test \ No newline at end of file +} // namespace atom::extra::pugixml::test diff --git a/tests/extra/pugixml/test_xml_node_wrapper.hpp b/tests/extra/pugixml/test_xml_node_wrapper.hpp index 600727ea..b102403e 100644 --- a/tests/extra/pugixml/test_xml_node_wrapper.hpp +++ b/tests/extra/pugixml/test_xml_node_wrapper.hpp @@ -419,4 +419,4 @@ TEST_F(XmlNodeWrapperTest, CompileTimeStrings) { EXPECT_EQ(view.size(), 4); } -} // namespace atom::extra::pugixml::test \ No newline at end of file +} // namespace atom::extra::pugixml::test diff --git a/tests/extra/pugixml/test_xml_query.hpp b/tests/extra/pugixml/test_xml_query.hpp index 62d68f9e..fe8040bd 100644 --- a/tests/extra/pugixml/test_xml_query.hpp +++ b/tests/extra/pugixml/test_xml_query.hpp @@ -533,4 +533,4 @@ TEST_F(XmlQueryTest, CombinedOperations) { EXPECT_EQ(title->text(), "The Lord of the Rings"); } -} // namespace atom::extra::pugixml::test \ No newline at end of file +} // namespace atom::extra::pugixml::test diff --git a/tests/extra/uv/test_coro.hpp b/tests/extra/uv/test_coro.hpp index a485b87b..92eaf594 100644 --- a/tests/extra/uv/test_coro.hpp +++ b/tests/extra/uv/test_coro.hpp @@ -861,4 +861,4 @@ TEST_F(UvCoroTest, SchedulerTest) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/extra/uv/test_message_bus.hpp b/tests/extra/uv/test_message_bus.hpp index d6c160e1..4371e064 100644 --- a/tests/extra/uv/test_message_bus.hpp +++ b/tests/extra/uv/test_message_bus.hpp @@ -343,4 +343,4 @@ TEST_F(MessageBusTest, MessageEnvelopeMetadataTest) { EXPECT_EQ(envelope.metadata.find("source"), envelope.metadata.end()); } -} // namespace msgbus::test \ No newline at end of file +} // namespace msgbus::test diff --git a/tests/extra/uv/test_subprocess.hpp b/tests/extra/uv/test_subprocess.hpp index 47d0650c..5ba7cadb 100644 --- a/tests/extra/uv/test_subprocess.hpp +++ b/tests/extra/uv/test_subprocess.hpp @@ -601,4 +601,4 @@ TEST_F(UvProcessTest, DetachedProcess) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/image/test_fits_header.hpp b/tests/image/test_fits_header.hpp index d4473239..52b420af 100644 --- a/tests/image/test_fits_header.hpp +++ b/tests/image/test_fits_header.hpp @@ -37,15 +37,15 @@ TEST_F(FITSHeaderTest, AddAndGetKeyword) { EXPECT_EQ(header.getKeywordValue("SIMPLE"), "T"); EXPECT_EQ(header.getKeywordValue("BITPIX"), "16"); EXPECT_EQ(header.getKeywordValue("NAXIS"), "2"); - + // Add a new keyword header.addKeyword("OBJECT", "M31"); EXPECT_EQ(header.getKeywordValue("OBJECT"), "M31"); - + // Update an existing keyword header.addKeyword("BITPIX", "32"); EXPECT_EQ(header.getKeywordValue("BITPIX"), "32"); - + // Add a keyword with a longer value std::string long_value = "This is a longer value with spaces and special chars: !@#$%^&*()"; header.addKeyword("COMMENT", long_value); @@ -57,7 +57,7 @@ TEST_F(FITSHeaderTest, HasKeyword) { EXPECT_TRUE(header.hasKeyword("SIMPLE")); EXPECT_TRUE(header.hasKeyword("BITPIX")); EXPECT_FALSE(header.hasKeyword("NONEXIST")); - + // Check case sensitivity EXPECT_FALSE(header.hasKeyword("simple")); // FITS keywords should be case-sensitive } @@ -67,7 +67,7 @@ TEST_F(FITSHeaderTest, RemoveKeyword) { EXPECT_TRUE(header.hasKeyword("BITPIX")); header.removeKeyword("BITPIX"); EXPECT_FALSE(header.hasKeyword("BITPIX")); - + // Removing non-existent keyword should not throw EXPECT_NO_THROW(header.removeKeyword("NONEXIST")); } @@ -75,17 +75,17 @@ TEST_F(FITSHeaderTest, RemoveKeyword) { // Test getting all keywords TEST_F(FITSHeaderTest, GetAllKeywords) { auto keywords = header.getAllKeywords(); - + // Check that expected keywords are present EXPECT_THAT(keywords, ::testing::Contains("SIMPLE")); EXPECT_THAT(keywords, ::testing::Contains("BITPIX")); EXPECT_THAT(keywords, ::testing::Contains("NAXIS")); EXPECT_THAT(keywords, ::testing::Contains("NAXIS1")); EXPECT_THAT(keywords, ::testing::Contains("NAXIS2")); - + // Check that non-existent keywords are not present EXPECT_THAT(keywords, ::testing::Not(::testing::Contains("NONEXIST"))); - + // Check the total count EXPECT_EQ(keywords.size(), 5); } @@ -94,7 +94,7 @@ TEST_F(FITSHeaderTest, GetAllKeywords) { TEST_F(FITSHeaderTest, AddAndGetComments) { header.addComment("This is a test comment"); header.addComment("Another comment"); - + auto comments = header.getComments(); EXPECT_EQ(comments.size(), 2); EXPECT_THAT(comments, ::testing::Contains("This is a test comment")); @@ -105,9 +105,9 @@ TEST_F(FITSHeaderTest, AddAndGetComments) { TEST_F(FITSHeaderTest, ClearComments) { header.addComment("Comment 1"); header.addComment("Comment 2"); - + EXPECT_EQ(header.getComments().size(), 2); - + header.clearComments(); EXPECT_EQ(header.getComments().size(), 0); } @@ -120,15 +120,15 @@ TEST_F(FITSHeaderTest, GetKeywordValueError) { // Test serialization TEST_F(FITSHeaderTest, Serialization) { std::vector data = header.serialize(); - + // Check size is a multiple of FITS_HEADER_UNIT_SIZE EXPECT_EQ(data.size() % FITSHeader::FITS_HEADER_UNIT_SIZE, 0); - + // Check for expected patterns in the serialized data EXPECT_TRUE(containsPattern(data, "SIMPLE = T")); EXPECT_TRUE(containsPattern(data, "BITPIX = 16")); EXPECT_TRUE(containsPattern(data, "NAXIS = 2")); - + // Check for END keyword at the end std::string end_pattern = "END "; bool has_end = false; @@ -145,18 +145,18 @@ TEST_F(FITSHeaderTest, Serialization) { TEST_F(FITSHeaderTest, Deserialization) { // Serialize the current header std::vector data = header.serialize(); - + // Create a new header and deserialize into it FITSHeader new_header; new_header.deserialize(data); - + // Check that deserialized header has the same keywords EXPECT_TRUE(new_header.hasKeyword("SIMPLE")); EXPECT_TRUE(new_header.hasKeyword("BITPIX")); EXPECT_TRUE(new_header.hasKeyword("NAXIS")); EXPECT_TRUE(new_header.hasKeyword("NAXIS1")); EXPECT_TRUE(new_header.hasKeyword("NAXIS2")); - + // Check that values match EXPECT_EQ(new_header.getKeywordValue("SIMPLE"), "T"); EXPECT_EQ(new_header.getKeywordValue("BITPIX"), "16"); @@ -168,11 +168,11 @@ TEST_F(FITSHeaderTest, DeserializationErrors) { // Test with empty data std::vector empty_data; EXPECT_THROW(header.deserialize(empty_data), FITSHeaderException); - + // Test with data that's not a multiple of FITS_HEADER_CARD_SIZE std::vector invalid_size_data(FITSHeader::FITS_HEADER_CARD_SIZE - 1, ' '); EXPECT_THROW(header.deserialize(invalid_size_data), FITSHeaderException); - + // Test with data that doesn't contain an END keyword std::vector no_end_data(FITSHeader::FITS_HEADER_UNIT_SIZE, ' '); EXPECT_THROW(header.deserialize(no_end_data), FITSHeaderException); @@ -185,7 +185,7 @@ TEST_F(FITSHeaderTest, LongKeywordsAndValues) { header.addKeyword(long_keyword, "value"); EXPECT_FALSE(header.hasKeyword(long_keyword)); EXPECT_TRUE(header.hasKeyword(long_keyword.substr(0, 8))); - + // Value longer than 72 chars should be truncated std::string long_value(100, 'X'); // 100 X characters header.addKeyword("LONGVAL", long_value); @@ -197,15 +197,15 @@ TEST_F(FITSHeaderTest, SpecialKeywordFormats) { // Test HIERARCH convention for long keywords header.addKeyword("HIERARCH ESO DET CHIP TEMP", "-120.0"); EXPECT_TRUE(header.hasKeyword("HIERARCH")); - + // Test with string value (should be quoted) header.addKeyword("TELESCOP", "'JWST'"); EXPECT_EQ(header.getKeywordValue("TELESCOP"), "'JWST'"); - + // Test with boolean value header.addKeyword("FLAG", "T"); EXPECT_EQ(header.getKeywordValue("FLAG"), "T"); - + // Test with numeric value header.addKeyword("EXPTIME", "1200.5"); EXPECT_EQ(header.getKeywordValue("EXPTIME"), "1200.5"); @@ -214,11 +214,11 @@ TEST_F(FITSHeaderTest, SpecialKeywordFormats) { // Test KeywordRecord constructor TEST_F(FITSHeaderTest, KeywordRecordConstructor) { FITSHeader::KeywordRecord record("TEST", "value"); - + // Check keyword is stored correctly std::array expected_keyword{'T', 'E', 'S', 'T', 0, 0, 0, 0}; EXPECT_EQ(record.keyword, expected_keyword); - + // Check value is stored correctly std::array expected_value{}; std::fill(expected_value.begin(), expected_value.end(), 0); @@ -230,24 +230,24 @@ TEST_F(FITSHeaderTest, KeywordRecordConstructor) { TEST_F(FITSHeaderTest, ExtensiveFITSHeader) { // Create a header with many keywords to test scaling behavior FITSHeader large_header; - + // Add 100 keywords for (int i = 0; i < 100; i++) { std::string keyword = "KEY" + std::to_string(i); std::string value = "value" + std::to_string(i); large_header.addKeyword(keyword.substr(0, 8), value); } - + // Check all keywords exist for (int i = 0; i < 100; i++) { std::string keyword = "KEY" + std::to_string(i); EXPECT_TRUE(large_header.hasKeyword(keyword.substr(0, 8))); } - + // Check serialization size std::vector data = large_header.serialize(); - int expected_size = ((100 + 1) * FITSHeader::FITS_HEADER_CARD_SIZE + FITSHeader::FITS_HEADER_UNIT_SIZE - 1) - / FITSHeader::FITS_HEADER_UNIT_SIZE + int expected_size = ((100 + 1) * FITSHeader::FITS_HEADER_CARD_SIZE + FITSHeader::FITS_HEADER_UNIT_SIZE - 1) + / FITSHeader::FITS_HEADER_UNIT_SIZE * FITSHeader::FITS_HEADER_UNIT_SIZE; EXPECT_EQ(data.size(), expected_size); } @@ -259,19 +259,19 @@ TEST_F(FITSHeaderTest, RequiredFITSKeywords) { minimal_header.addKeyword("SIMPLE", "T"); minimal_header.addKeyword("BITPIX", "16"); minimal_header.addKeyword("NAXIS", "0"); - + // Serialize and check std::vector data = minimal_header.serialize(); EXPECT_TRUE(containsPattern(data, "SIMPLE = T")); EXPECT_TRUE(containsPattern(data, "BITPIX = 16")); EXPECT_TRUE(containsPattern(data, "NAXIS = 0")); - + // Required keywords should be in the correct order std::string data_str(data.begin(), data.end()); size_t simple_pos = data_str.find("SIMPLE"); size_t bitpix_pos = data_str.find("BITPIX"); size_t naxis_pos = data_str.find("NAXIS"); - + EXPECT_LT(simple_pos, bitpix_pos); EXPECT_LT(bitpix_pos, naxis_pos); } @@ -280,10 +280,10 @@ TEST_F(FITSHeaderTest, RequiredFITSKeywords) { TEST_F(FITSHeaderTest, ContinueKeyword) { // Create a header with a long string that requires CONTINUE FITSHeader header_with_continue; - + std::string long_string(150, 'A'); // 150 'A' characters header_with_continue.addKeyword("HISTORY", long_string); - + // Serialize and check for CONTINUE std::vector data = header_with_continue.serialize(); EXPECT_TRUE(containsPattern(data, "HISTORY ")); @@ -294,12 +294,12 @@ TEST_F(FITSHeaderTest, ContinueKeyword) { TEST_F(FITSHeaderTest, CommentVsHistory) { header.addComment("This is a comment"); header.addKeyword("HISTORY", "This is a history entry"); - + // Serialize and check both are present std::vector data = header.serialize(); EXPECT_TRUE(containsPattern(data, "COMMENT This is a comment")); EXPECT_TRUE(containsPattern(data, "HISTORY This is a history entry")); - + // COMMENT should not appear in normal getAllKeywords list auto keywords = header.getAllKeywords(); EXPECT_THAT(keywords, ::testing::Contains("HISTORY")); @@ -309,7 +309,7 @@ TEST_F(FITSHeaderTest, CommentVsHistory) { TEST_F(FITSHeaderTest, EmptyValues) { header.addKeyword("EMPTY", ""); EXPECT_EQ(header.getKeywordValue("EMPTY"), ""); - + // Serialize and check std::vector data = header.serialize(); EXPECT_TRUE(containsPattern(data, "EMPTY =")); @@ -318,7 +318,7 @@ TEST_F(FITSHeaderTest, EmptyValues) { // Test round-trip with all kinds of values TEST_F(FITSHeaderTest, RoundTripValues) { FITSHeader test_header; - + // Add various types of values test_header.addKeyword("BOOLEAN", "T"); test_header.addKeyword("INTEGER", "42"); @@ -327,12 +327,12 @@ TEST_F(FITSHeaderTest, RoundTripValues) { test_header.addKeyword("DATE", "'2023-01-01T12:00:00'"); test_header.addKeyword("EMPTY", ""); test_header.addComment("Test comment"); - + // Serialize and deserialize std::vector data = test_header.serialize(); FITSHeader deserialized; deserialized.deserialize(data); - + // Check all values survived round-trip EXPECT_EQ(deserialized.getKeywordValue("BOOLEAN"), "T"); EXPECT_EQ(deserialized.getKeywordValue("INTEGER"), "42"); @@ -346,26 +346,26 @@ TEST_F(FITSHeaderTest, RoundTripValues) { // Test with multi-line serialization TEST_F(FITSHeaderTest, MultilineComment) { header.addComment("Line 1\nLine 2\nLine 3"); - + auto comments = header.getComments(); EXPECT_EQ(comments.size(), 1); EXPECT_EQ(comments[0], "Line 1\nLine 2\nLine 3"); - + // Serialize and check - should be flattened or split into multiple COMMENT lines std::vector data = header.serialize(); - + // Either approach is valid, just make sure the data is preserved FITSHeader deserialized; deserialized.deserialize(data); auto deserialized_comments = deserialized.getComments(); - + std::string original = comments[0]; std::string reconstructed; for (const auto& c : deserialized_comments) { if (!reconstructed.empty()) reconstructed += "\n"; reconstructed += c; } - + // Check that content is preserved, even if format changes EXPECT_TRUE(reconstructed.find("Line 1") != std::string::npos); EXPECT_TRUE(reconstructed.find("Line 2") != std::string::npos); @@ -377,4 +377,4 @@ TEST_F(FITSHeaderTest, MultilineComment) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/image/test_hdu.hpp b/tests/image/test_hdu.hpp index d886541d..57d0f208 100644 --- a/tests/image/test_hdu.hpp +++ b/tests/image/test_hdu.hpp @@ -21,12 +21,12 @@ namespace fs = std::filesystem; // Helper function to create a temporary FITS file for testing inline std::string createTempFitsFile(int width = 10, int height = 10, int channels = 1) { // Create a temporary file - std::string tempFilePath = (fs::temp_directory_path() / + std::string tempFilePath = (fs::temp_directory_path() / fs::path("test_hdu_temp_" + std::to_string(std::random_device{}()) + ".fits")).string(); - + // Create a simple FITS file with basic header std::ofstream outFile(tempFilePath, std::ios::binary); - + // Write FITS header outFile << "SIMPLE = T / Standard FITS format" << std::string(80-44, ' ') << std::endl; outFile << "BITPIX = 32 / Bits per pixel" << std::string(80-42, ' ') << std::endl; @@ -37,22 +37,22 @@ inline std::string createTempFitsFile(int width = 10, int height = 10, int chann outFile << "NAXIS3 = " << std::setw(2) << channels << " / Channels" << std::string(80-41, ' ') << std::endl; } outFile << "END" << std::string(80-3, ' ') << std::endl; - + // Pad header to multiple of 2880 bytes int headerBlocks = 1; // Start with one for the header we've already written int bytesWritten = headerBlocks * 2880; int paddingRequired = bytesWritten - (7 * 80); // 7 header cards written so far outFile << std::string(paddingRequired, ' '); - + // Write simple data (all zeros) int dataSize = width * height * channels * sizeof(int32_t); std::vector dummyData(width * height * channels, 0); outFile.write(reinterpret_cast(dummyData.data()), dataSize); - + // Pad data to multiple of 2880 bytes int dataPaddingRequired = (2880 - (dataSize % 2880)) % 2880; outFile << std::string(dataPaddingRequired, '\0'); - + outFile.close(); return tempFilePath; } @@ -65,19 +65,19 @@ class ImageHDUTest : public ::testing::Test { tempFilePaths.push_back(createTempFitsFile(10, 10, 3)); // RGB image tempFilePaths.push_back(createTempFitsFile(100, 100, 1)); // Larger image } - + void TearDown() override { // Clean up all created temp files for (const auto& path : tempFilePaths) { std::remove(path.c_str()); } } - + // Fill an ImageHDU with test data template void fillTestData(ImageHDU& hdu, int width, int height, int channels) { hdu.setImageSize(width, height, channels); - + // Fill with test pattern for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { @@ -88,7 +88,7 @@ class ImageHDUTest : public ::testing::Test { } } } - + // Create an ImageHDU with test pattern and specific data type template std::unique_ptr createTestImageHDU(int width, int height, int channels = 1) { @@ -96,7 +96,7 @@ class ImageHDUTest : public ::testing::Test { fillTestData(*hdu, width, height, channels); return hdu; } - + std::vector tempFilePaths; }; @@ -104,9 +104,9 @@ class ImageHDUTest : public ::testing::Test { TEST_F(ImageHDUTest, ReadHDUFromFile) { ImageHDU hdu; std::ifstream file(tempFilePaths[0], std::ios::binary); - + ASSERT_NO_THROW(hdu.readHDU(file)); - + auto [width, height, channels] = hdu.getImageSize(); EXPECT_EQ(width, 10); EXPECT_EQ(height, 10); @@ -117,9 +117,9 @@ TEST_F(ImageHDUTest, ReadHDUFromFile) { TEST_F(ImageHDUTest, ReadMultiChannelHDU) { ImageHDU hdu; std::ifstream file(tempFilePaths[1], std::ios::binary); - + ASSERT_NO_THROW(hdu.readHDU(file)); - + auto [width, height, channels] = hdu.getImageSize(); EXPECT_EQ(width, 10); EXPECT_EQ(height, 10); @@ -131,24 +131,24 @@ TEST_F(ImageHDUTest, ReadMultiChannelHDU) { TEST_F(ImageHDUTest, WriteHDUToFile) { // Create a test HDU with data auto hdu = createTestImageHDU(20, 15, 1); - + // Write to a new file std::string outputPath = (fs::temp_directory_path() / "test_hdu_write.fits").string(); std::ofstream outputFile(outputPath, std::ios::binary); - + ASSERT_NO_THROW(hdu->writeHDU(outputFile)); outputFile.close(); - + // Read it back to verify ImageHDU readHdu; std::ifstream inputFile(outputPath, std::ios::binary); ASSERT_NO_THROW(readHdu.readHDU(inputFile)); - + auto [width, height, channels] = readHdu.getImageSize(); EXPECT_EQ(width, 20); EXPECT_EQ(height, 15); EXPECT_EQ(channels, 1); - + // Clean up std::remove(outputPath.c_str()); } @@ -158,11 +158,11 @@ TEST_F(ImageHDUTest, HeaderKeywords) { ImageHDU hdu; std::ifstream file(tempFilePaths[0], std::ios::binary); hdu.readHDU(file); - + // Set and get a keyword hdu.setHeaderKeyword("OBSERVER", "Test User"); EXPECT_EQ(hdu.getHeaderKeyword("OBSERVER"), "Test User"); - + // Should have standard FITS keywords EXPECT_EQ(hdu.getHeaderKeyword("SIMPLE"), "T"); EXPECT_EQ(hdu.getHeaderKeyword("BITPIX"), "32"); @@ -172,16 +172,16 @@ TEST_F(ImageHDUTest, HeaderKeywords) { // Test setting and getting image dimensions TEST_F(ImageHDUTest, ImageDimensions) { ImageHDU hdu; - + ASSERT_NO_THROW(hdu.setImageSize(30, 40, 2)); - + auto [width, height, channels] = hdu.getImageSize(); EXPECT_EQ(width, 30); EXPECT_EQ(height, 40); EXPECT_EQ(channels, 2); EXPECT_TRUE(hdu.isColor()); EXPECT_EQ(hdu.getChannelCount(), 2); - + // Test invalid dimensions EXPECT_THROW(hdu.setImageSize(-5, 40), std::invalid_argument); EXPECT_THROW(hdu.setImageSize(30, 0), std::invalid_argument); @@ -191,16 +191,16 @@ TEST_F(ImageHDUTest, ImageDimensions) { // Test pixel access operations for different data types TEST_F(ImageHDUTest, PixelAccess_Int32) { auto hdu = createTestImageHDU(15, 10); - + // Check a few pixels EXPECT_EQ(hdu->getPixel(5, 5), (5 + 5 * 2) % 255); EXPECT_EQ(hdu->getPixel(0, 0), 0); EXPECT_EQ(hdu->getPixel(9, 9), (9 + 9 * 2) % 255); - + // Modify a pixel hdu->setPixel(5, 5, 123); EXPECT_EQ(hdu->getPixel(5, 5), 123); - + // Out of bounds access EXPECT_THROW(hdu->getPixel(15, 5), std::out_of_range); EXPECT_THROW(hdu->getPixel(5, 15), std::out_of_range); @@ -209,32 +209,32 @@ TEST_F(ImageHDUTest, PixelAccess_Int32) { TEST_F(ImageHDUTest, PixelAccess_Float) { auto hdu = createTestImageHDU(15, 10); - + // Check a few pixels EXPECT_FLOAT_EQ(hdu->getPixel(5, 5), static_cast((5 + 5 * 2) % 255)); EXPECT_FLOAT_EQ(hdu->getPixel(0, 0), 0.0f); - + // Modify a pixel hdu->setPixel(5, 5, 123.45f); EXPECT_FLOAT_EQ(hdu->getPixel(5, 5), 123.45f); - + // Invalid channel access EXPECT_THROW(hdu->getPixel(5, 5, 1), std::out_of_range); } TEST_F(ImageHDUTest, PixelAccess_Double) { auto hdu = createTestImageHDU(15, 10, 3); - + // Check multi-channel access for (int c = 0; c < 3; ++c) { EXPECT_DOUBLE_EQ(hdu->getPixel(5, 5, c), static_cast((5 + 5 * 2) % 255)); } - + // Modify different channels hdu->setPixel(5, 5, 100.5, 0); hdu->setPixel(5, 5, 200.5, 1); hdu->setPixel(5, 5, 300.5, 2); - + EXPECT_DOUBLE_EQ(hdu->getPixel(5, 5, 0), 100.5); EXPECT_DOUBLE_EQ(hdu->getPixel(5, 5, 1), 200.5); EXPECT_DOUBLE_EQ(hdu->getPixel(5, 5, 2), 300.5); @@ -243,15 +243,15 @@ TEST_F(ImageHDUTest, PixelAccess_Double) { // Test image statistics computation TEST_F(ImageHDUTest, ComputeImageStats_Int) { auto hdu = createTestImageHDU(20, 10); - + auto stats = hdu->computeImageStats(); - + // Check basic stats properties EXPECT_LE(stats.min, stats.max); EXPECT_GE(stats.mean, static_cast(stats.min)); EXPECT_LE(stats.mean, static_cast(stats.max)); EXPECT_GE(stats.stddev, 0.0); - + // For our pattern, we know some properties EXPECT_EQ(stats.min, 0); EXPECT_EQ(stats.max, 57); // (19 + 19*2) % 255 = 57 @@ -259,17 +259,17 @@ TEST_F(ImageHDUTest, ComputeImageStats_Int) { TEST_F(ImageHDUTest, ComputeImageStats_Float) { auto hdu = createTestImageHDU(20, 10, 2); - + // Check stats for each channel for (int channel = 0; channel < 2; ++channel) { auto stats = hdu->computeImageStats(channel); - + EXPECT_FLOAT_EQ(stats.min, 0.0f); EXPECT_FLOAT_EQ(stats.max, 57.0f); // (19 + 19*2) % 255 = 57 EXPECT_GT(stats.mean, 0.0); EXPECT_GT(stats.stddev, 0.0); } - + // Invalid channel EXPECT_THROW(hdu->computeImageStats(2), std::out_of_range); } @@ -277,25 +277,25 @@ TEST_F(ImageHDUTest, ComputeImageStats_Float) { // Test convolution filtering TEST_F(ImageHDUTest, ApplyFilter) { auto hdu = createTestImageHDU(20, 10); - + // Create a simple box blur kernel (3x3) std::vector kernelData = { 1.0/9.0, 1.0/9.0, 1.0/9.0, 1.0/9.0, 1.0/9.0, 1.0/9.0, 1.0/9.0, 1.0/9.0, 1.0/9.0 }; - + std::vector> kernel; for (int i = 0; i < 3; ++i) { kernel.push_back(std::span(&kernelData[i*3], 3)); } - + // Store original value for comparison float originalValue = hdu->getPixel(5, 5); - + // Apply filter ASSERT_NO_THROW(hdu->applyFilter(kernel)); - + // After box blur, center pixels should be the average of their neighborhood // But exact equality can be affected by boundary conditions, so we just verify it changed EXPECT_NE(hdu->getPixel(5, 5), originalValue); @@ -304,19 +304,19 @@ TEST_F(ImageHDUTest, ApplyFilter) { // Test parallel filtering TEST_F(ImageHDUTest, ApplyFilterParallel) { auto hdu = createTestImageHDU(50, 50); // Larger image for parallel processing - + // Create a simple box blur kernel (3x3) std::vector kernelData = { 1.0/9.0, 1.0/9.0, 1.0/9.0, 1.0/9.0, 1.0/9.0, 1.0/9.0, 1.0/9.0, 1.0/9.0, 1.0/9.0 }; - + std::vector> kernel; for (int i = 0; i < 3; ++i) { kernel.push_back(std::span(&kernelData[i*3], 3)); } - + // Store original values at several positions std::vector originalValues; for (int y = 10; y < 40; y += 10) { @@ -324,10 +324,10 @@ TEST_F(ImageHDUTest, ApplyFilterParallel) { originalValues.push_back(hdu->getPixel(x, y)); } } - + // Apply parallel filter ASSERT_NO_THROW(hdu->applyFilterParallel(kernel)); - + // Check that values have changed int idx = 0; for (int y = 10; y < 40; y += 10) { @@ -340,21 +340,21 @@ TEST_F(ImageHDUTest, ApplyFilterParallel) { // Test image resizing TEST_F(ImageHDUTest, Resize) { auto hdu = createTestImageHDU(20, 10); - + // Resize to larger dimensions ASSERT_NO_THROW(hdu->resize(40, 20)); - + auto [width, height, channels] = hdu->getImageSize(); EXPECT_EQ(width, 40); EXPECT_EQ(height, 20); - + // Resize to smaller dimensions ASSERT_NO_THROW(hdu->resize(10, 5)); - + std::tie(width, height, channels) = hdu->getImageSize(); EXPECT_EQ(width, 10); EXPECT_EQ(height, 5); - + // Invalid dimensions EXPECT_THROW(hdu->resize(0, 20), std::invalid_argument); EXPECT_THROW(hdu->resize(10, -5), std::invalid_argument); @@ -363,17 +363,17 @@ TEST_F(ImageHDUTest, Resize) { // Test thumbnail creation TEST_F(ImageHDUTest, CreateThumbnail) { auto hdu = createTestImageHDU(100, 50); - + // Create a thumbnail with max size 20 auto thumbnail = hdu->createThumbnail(20); ASSERT_NE(thumbnail, nullptr); - + auto [width, height, channels] = thumbnail->getImageSize(); - + // The width should be 20 and height should be proportionally scaled EXPECT_EQ(width, 20); EXPECT_EQ(height, 10); // 50/100 * 20 = 10 - + // Test with invalid size EXPECT_THROW(hdu->createThumbnail(0), std::invalid_argument); } @@ -381,23 +381,23 @@ TEST_F(ImageHDUTest, CreateThumbnail) { // Test ROI extraction TEST_F(ImageHDUTest, ExtractROI) { auto hdu = createTestImageHDU(30, 20); - + // Extract a region auto roi = hdu->extractROI(5, 5, 10, 8); ASSERT_NE(roi, nullptr); - + auto [width, height, channels] = roi->getImageSize(); EXPECT_EQ(width, 10); EXPECT_EQ(height, 8); - + // Check that the ROI data matches the original in that region for (int y = 0; y < 8; ++y) { for (int x = 0; x < 10; ++x) { - EXPECT_EQ(roi->getPixel(x, y), + EXPECT_EQ(roi->getPixel(x, y), hdu->getPixel(x + 5, y + 5)); } } - + // Test invalid ROI parameters EXPECT_THROW(hdu->extractROI(-1, 5, 10, 8), std::out_of_range); EXPECT_THROW(hdu->extractROI(5, 5, 50, 8), std::out_of_range); @@ -407,13 +407,13 @@ TEST_F(ImageHDUTest, ExtractROI) { // Test async statistics computation TEST_F(ImageHDUTest, ComputeImageStatsAsync) { auto hdu = createTestImageHDU(100, 100); // Larger image for async test - + // Compute stats asynchronously auto statsTask = hdu->computeImageStatsAsync(); - + // Get the result auto stats = statsTask.get_result(); - + // Check basic stats properties EXPECT_LE(stats.min, stats.max); EXPECT_GE(stats.mean, static_cast(stats.min)); @@ -425,26 +425,26 @@ TEST_F(ImageHDUTest, ComputeImageStatsAsync) { TEST_F(ImageHDUTest, BlendImage) { auto hdu1 = createTestImageHDU(20, 10); auto hdu2 = createTestImageHDU(20, 10); - + // Modify hdu2 to have different values for (int y = 0; y < 10; ++y) { for (int x = 0; x < 20; ++x) { hdu2->setPixel(x, y, 200.0f); } } - + // Blend with 50% of each ASSERT_NO_THROW(hdu1->blendImage(*hdu2, 0.5)); - + // Check a sample point - should be halfway between original and 200 float originalValue = static_cast((5 + 5 * 2) % 255); float expectedValue = originalValue * 0.5f + 200.0f * 0.5f; EXPECT_FLOAT_EQ(hdu1->getPixel(5, 5), expectedValue); - + // Test invalid alpha EXPECT_THROW(hdu1->blendImage(*hdu2, -0.1), std::invalid_argument); EXPECT_THROW(hdu1->blendImage(*hdu2, 1.5), std::invalid_argument); - + // Test incompatible images auto hdu3 = createTestImageHDU(30, 10); EXPECT_THROW(hdu1->blendImage(*hdu3, 0.5), ImageProcessingException); @@ -453,19 +453,19 @@ TEST_F(ImageHDUTest, BlendImage) { // Test mathematical operations TEST_F(ImageHDUTest, ApplyMathOperation) { auto hdu = createTestImageHDU(20, 10); - + // Apply a multiply-by-2 operation ASSERT_NO_THROW(hdu->applyMathOperation([](float val) { return val * 2.0f; })); - + // Check a sample point float originalValue = static_cast((5 + 5 * 2) % 255); EXPECT_FLOAT_EQ(hdu->getPixel(5, 5), originalValue * 2.0f); - + // Apply a complex operation - ASSERT_NO_THROW(hdu->applyMathOperation([](float val) { - return std::sin(val) * 100.0f; + ASSERT_NO_THROW(hdu->applyMathOperation([](float val) { + return std::sin(val) * 100.0f; })); - + // Check the result is changed EXPECT_NE(hdu->getPixel(5, 5), originalValue * 2.0f); } @@ -473,13 +473,13 @@ TEST_F(ImageHDUTest, ApplyMathOperation) { // Test histogram computation TEST_F(ImageHDUTest, ComputeHistogram) { auto hdu = createTestImageHDU(50, 50); - + // Compute a histogram with 10 bins auto histogram = hdu->computeHistogram(10); - + // Check basic properties EXPECT_EQ(histogram.size(), 10); - + // The sum of all bins should equal the number of pixels double sum = 0.0; for (double binCount : histogram) { @@ -487,7 +487,7 @@ TEST_F(ImageHDUTest, ComputeHistogram) { EXPECT_GE(binCount, 0.0); // Bin counts should be non-negative } EXPECT_EQ(sum, 50 * 50); - + // Test invalid bin count EXPECT_THROW(hdu->computeHistogram(0), std::invalid_argument); } @@ -495,16 +495,16 @@ TEST_F(ImageHDUTest, ComputeHistogram) { // Test histogram equalization TEST_F(ImageHDUTest, EqualizeHistogram) { auto hdu = createTestImageHDU(50, 50); - + // Calculate histogram before equalization auto histBefore = hdu->computeHistogram(256); - + // Perform equalization ASSERT_NO_THROW(hdu->equalizeHistogram()); - + // Calculate histogram after equalization auto histAfter = hdu->computeHistogram(256); - + // Histograms should be different after equalization bool histogramChanged = false; for (size_t i = 0; i < histBefore.size(); ++i) { @@ -519,16 +519,16 @@ TEST_F(ImageHDUTest, EqualizeHistogram) { // Test edge detection TEST_F(ImageHDUTest, DetectEdges) { auto hdu = createTestImageHDU(50, 50); - + // Store original value float originalValue = hdu->getPixel(25, 25); - + // Apply Sobel edge detection ASSERT_NO_THROW(hdu->detectEdges("sobel")); - + // Values should change after edge detection EXPECT_NE(hdu->getPixel(25, 25), originalValue); - + // Test invalid method EXPECT_THROW(hdu->detectEdges("invalid_method"), std::invalid_argument); } @@ -536,7 +536,7 @@ TEST_F(ImageHDUTest, DetectEdges) { // Test compression functions TEST_F(ImageHDUTest, CompressionDecompression) { auto hdu = createTestImageHDU(50, 50); - + // Store original data std::vector originalData; for (int y = 0; y < 50; ++y) { @@ -544,17 +544,17 @@ TEST_F(ImageHDUTest, CompressionDecompression) { originalData.push_back(hdu->getPixel(x, y)); } } - + // Compress with RLE ASSERT_NO_THROW(hdu->compressData("rle")); - + // Check compression ratio double ratio = hdu->computeCompressionRatio(); EXPECT_GT(ratio, 1.0); // Should achieve some compression - + // Decompress ASSERT_NO_THROW(hdu->decompressData()); - + // Verify data is preserved int idx = 0; for (int y = 0; y < 50; ++y) { @@ -562,7 +562,7 @@ TEST_F(ImageHDUTest, CompressionDecompression) { EXPECT_FLOAT_EQ(hdu->getPixel(x, y), originalData[idx++]); } } - + // Test invalid algorithm EXPECT_THROW(hdu->compressData("invalid_algorithm"), std::invalid_argument); } @@ -570,7 +570,7 @@ TEST_F(ImageHDUTest, CompressionDecompression) { // Test noise addition and removal TEST_F(ImageHDUTest, NoiseAdditionAndRemoval) { auto hdu = createTestImageHDU(30, 30); - + // Store original data std::vector originalData; for (int y = 0; y < 30; ++y) { @@ -578,10 +578,10 @@ TEST_F(ImageHDUTest, NoiseAdditionAndRemoval) { originalData.push_back(hdu->getPixel(x, y)); } } - + // Add Gaussian noise ASSERT_NO_THROW(hdu->addNoise("gaussian", 10.0)); - + // Verify data changed bool dataChanged = false; int idx = 0; @@ -593,10 +593,10 @@ TEST_F(ImageHDUTest, NoiseAdditionAndRemoval) { } } EXPECT_TRUE(dataChanged); - + // Remove noise with median filter ASSERT_NO_THROW(hdu->removeNoise("median", 3)); - + // Test invalid parameters EXPECT_THROW(hdu->addNoise("invalid_noise", 10.0), std::invalid_argument); EXPECT_THROW(hdu->removeNoise("median", 0), std::invalid_argument); @@ -605,16 +605,16 @@ TEST_F(ImageHDUTest, NoiseAdditionAndRemoval) { // Test Fourier transform and filtering TEST_F(ImageHDUTest, FourierTransformAndFiltering) { auto hdu = createTestImageHDU(32, 32); // Power of 2 size for FFT - + // Apply forward FFT ASSERT_NO_THROW(hdu->applyFourierTransform(false)); - + // Apply lowpass filter in frequency domain ASSERT_NO_THROW(hdu->applyFrequencyFilter("lowpass", 0.5)); - + // Apply inverse FFT to get back to spatial domain ASSERT_NO_THROW(hdu->applyFourierTransform(true)); - + // Test invalid parameters EXPECT_THROW(hdu->applyFrequencyFilter("invalid_filter", 0.5), std::invalid_argument); } @@ -622,10 +622,10 @@ TEST_F(ImageHDUTest, FourierTransformAndFiltering) { // Test auto-levels adjustment TEST_F(ImageHDUTest, AutoLevels) { auto hdu = createTestImageHDU(50, 50); - + // Apply auto-levels with custom black and white points ASSERT_NO_THROW(hdu->autoLevels(0.1, 0.9)); - + // Test invalid parameters EXPECT_THROW(hdu->autoLevels(-0.1, 0.9), std::invalid_argument); EXPECT_THROW(hdu->autoLevels(0.1, 1.1), std::invalid_argument); @@ -635,13 +635,13 @@ TEST_F(ImageHDUTest, AutoLevels) { // Test morphological operations TEST_F(ImageHDUTest, ApplyMorphology) { auto hdu = createTestImageHDU(50, 50); - + // Apply dilation ASSERT_NO_THROW(hdu->applyMorphology("dilate", 3)); - + // Apply erosion ASSERT_NO_THROW(hdu->applyMorphology("erode", 3)); - + // Test invalid parameters EXPECT_THROW(hdu->applyMorphology("invalid_op", 3), std::invalid_argument); EXPECT_THROW(hdu->applyMorphology("dilate", 4), std::invalid_argument); // Kernel size should be odd @@ -650,4 +650,4 @@ TEST_F(ImageHDUTest, ApplyMorphology) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/image/test_image_blob.hpp b/tests/image/test_image_blob.hpp index abd1fabe..3badb00f 100644 --- a/tests/image/test_image_blob.hpp +++ b/tests/image/test_image_blob.hpp @@ -50,7 +50,7 @@ TEST_F(BlobTest, DefaultConstructor) { TEST_F(BlobTest, ConstructorWithRawData) { blob b(test_data.data(), test_data.size()); EXPECT_EQ(b.size(), test_data.size()); - + // Check that data was copied correctly for (size_t i = 0; i < test_data.size(); ++i) { EXPECT_EQ(b[i], test_data[i]); @@ -77,7 +77,7 @@ TEST_F(BlobTest, CopyConstructor) { EXPECT_EQ(copy.getCols(), original.getCols()); EXPECT_EQ(copy.getChannels(), original.getChannels()); EXPECT_EQ(copy.getDepth(), original.getDepth()); - + // Check that data was copied correctly for (size_t i = 0; i < original.size(); ++i) { EXPECT_EQ(copy[i], original[i]); @@ -103,9 +103,9 @@ TEST_F(BlobTest, MoveConstructor) { TEST_F(BlobTest, ConstConversionConstructor) { blob mutable_blob(test_data.data(), test_data.size()); cblob const_blob(mutable_blob); - + EXPECT_EQ(const_blob.size(), mutable_blob.size()); - + // Check that data was copied correctly for (size_t i = 0; i < mutable_blob.size(); ++i) { EXPECT_EQ(const_blob[i], mutable_blob[i]); @@ -116,9 +116,9 @@ TEST_F(BlobTest, ConstConversionConstructor) { TEST_F(BlobTest, FastModeBlob) { std::vector data(test_data); fast_blob fb(data.data(), data.size()); - + EXPECT_EQ(fb.size(), data.size()); - + // Modify original data and check that fast_blob reflects the changes data[0] = std::byte{255}; EXPECT_EQ(fb[0], std::byte{255}); @@ -130,19 +130,19 @@ TEST_F(BlobTest, Slice) { b.rows_ = 2; b.cols_ = 6; // 2 pixels per row, 3 channels per pixel b.channels_ = 3; - + // Slice first row blob first_row = b.slice(0, 6); EXPECT_EQ(first_row.size(), 6); EXPECT_EQ(first_row[0], std::byte{10}); EXPECT_EQ(first_row[5], std::byte{60}); - + // Slice second row blob second_row = b.slice(6, 6); EXPECT_EQ(second_row.size(), 6); EXPECT_EQ(second_row[0], std::byte{70}); EXPECT_EQ(second_row[5], std::byte{120}); - + // Test out of bounds slice EXPECT_THROW(b.slice(10, 10), std::out_of_range); } @@ -152,18 +152,18 @@ TEST_F(BlobTest, EqualityOperator) { blob b1(test_data.data(), test_data.size()); blob b2(test_data.data(), test_data.size()); blob b3(test_data.data(), test_data.size() - 1); // Different size - + EXPECT_EQ(b1, b2); EXPECT_NE(b1, b3); - + // Modify b2 and check inequality b2[0] = std::byte{255}; EXPECT_NE(b1, b2); - + // Set b2 back to equal b1 b2[0] = b1[0]; EXPECT_EQ(b1, b2); - + // Change other properties and check inequality b2.rows_ = 3; EXPECT_NE(b1, b2); @@ -173,7 +173,7 @@ TEST_F(BlobTest, EqualityOperator) { TEST_F(BlobTest, Fill) { blob b(test_data.data(), test_data.size()); b.fill(std::byte{42}); - + for (size_t i = 0; i < b.size(); ++i) { EXPECT_EQ(b[i], std::byte{42}); } @@ -183,17 +183,17 @@ TEST_F(BlobTest, Fill) { TEST_F(BlobTest, AppendBlob) { blob b1(test_data.data(), 6); // First row blob b2(test_data.data() + 6, 6); // Second row - + b1.rows_ = 1; b1.cols_ = 6; b1.channels_ = 1; - + b2.rows_ = 1; b2.cols_ = 6; b2.channels_ = 1; - + b1.append(b2); - + EXPECT_EQ(b1.size(), 12); EXPECT_EQ(b1.getRows(), 2); EXPECT_EQ(b1[6], std::byte{70}); @@ -206,9 +206,9 @@ TEST_F(BlobTest, AppendRawData) { b.rows_ = 1; b.cols_ = 6; b.channels_ = 1; - + b.append(test_data.data() + 6, 6); // Append second row - + EXPECT_EQ(b.size(), 12); EXPECT_EQ(b.getRows(), 2); EXPECT_EQ(b[6], std::byte{70}); @@ -220,7 +220,7 @@ TEST_F(BlobTest, AllocateAndDeallocate) { blob b; b.allocate(10); EXPECT_EQ(b.size(), 10); - + b.deallocate(); EXPECT_EQ(b.size(), 0); } @@ -229,17 +229,17 @@ TEST_F(BlobTest, AllocateAndDeallocate) { TEST_F(BlobTest, XorOperation) { blob b1(test_data.data(), test_data.size()); blob b2(test_data.data(), test_data.size()); - + // Fill b2 with a constant value b2.fill(std::byte{255}); - + b1.xorWith(b2); - + // Check that each byte is now the XOR of the original and 255 for (size_t i = 0; i < test_data.size(); ++i) { EXPECT_EQ(b1[i], std::byte{static_cast(test_data[i]) ^ 255}); } - + // Test with different sized blobs blob b3(test_data.data(), test_data.size() - 1); EXPECT_THROW(b1.xorWith(b3), std::runtime_error); @@ -250,10 +250,10 @@ TEST_F(BlobTest, CompressionAndDecompression) { // Create a blob with repeated values that should compress well std::vector compressible_data(100, std::byte{42}); blob original(compressible_data.data(), compressible_data.size()); - + blob compressed = original.compress(); EXPECT_LT(compressed.size(), original.size()); - + blob decompressed = compressed.decompress(); EXPECT_EQ(decompressed.size(), original.size()); EXPECT_EQ(decompressed, original); @@ -265,17 +265,17 @@ TEST_F(BlobTest, SerializationAndDeserialization) { original.rows_ = 2; original.cols_ = 2; original.channels_ = 3; - + std::vector serialized = original.serialize(); blob deserialized = blob::deserialize(serialized); - + EXPECT_EQ(deserialized.size(), original.size()); - + // Check data equality for (size_t i = 0; i < original.size(); ++i) { EXPECT_EQ(deserialized[i], original[i]); } - + // Test with invalid data std::vector invalid_data(2, std::byte{0}); EXPECT_THROW(blob::deserialize(invalid_data), std::runtime_error); @@ -284,14 +284,14 @@ TEST_F(BlobTest, SerializationAndDeserialization) { // Test iteration methods TEST_F(BlobTest, Iteration) { blob b(test_data.data(), test_data.size()); - + // Test begin/end interface size_t i = 0; for (auto byte : b) { EXPECT_EQ(byte, test_data[i++]); } EXPECT_EQ(i, test_data.size()); - + // Test const begin/end interface const blob& const_b = b; i = 0; @@ -306,7 +306,7 @@ TEST_F(BlobTest, Iteration) { TEST_F(BlobTest, OpenCVIntegration) { // Create a test matrix cv::Mat mat(2, 2, CV_8UC3); - + // Fill with test data for (int i = 0; i < 2; ++i) { for (int j = 0; j < 2; ++j) { @@ -315,21 +315,21 @@ TEST_F(BlobTest, OpenCVIntegration) { } } } - + // Create blob from matrix blob b(mat); - + EXPECT_EQ(b.getRows(), 2); EXPECT_EQ(b.getCols(), 2); EXPECT_EQ(b.getChannels(), 3); EXPECT_EQ(b.size(), 12); - + // Convert back to matrix cv::Mat reconstructed = b.to_mat(); - + // Verify matrix equality EXPECT_TRUE(cv::countNonZero(mat != reconstructed) == 0); - + // Test image operations blob resized = b; resized.resize(4, 4); @@ -337,32 +337,32 @@ TEST_F(BlobTest, OpenCVIntegration) { EXPECT_EQ(resized.getCols(), 4); EXPECT_EQ(resized.getChannels(), 3); EXPECT_EQ(resized.size(), 48); - + // Test channel splitting and merging std::vector channels = b.split_channels(); EXPECT_EQ(channels.size(), 3); EXPECT_EQ(channels[0].getChannels(), 1); EXPECT_EQ(channels[0].size(), 4); - + blob merged = blob::merge_channels(channels); EXPECT_EQ(merged.getChannels(), 3); EXPECT_EQ(merged.size(), 12); EXPECT_EQ(merged, b); - + // Test filtering cv::Mat kernel = (cv::Mat_(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0); blob filtered = b; filtered.apply_filter(kernel); - + // Test rotation and flipping blob rotated = b; rotated.rotate(90); EXPECT_NE(rotated, b); - + blob flipped = b; flipped.flip(1); // Horizontal flip EXPECT_NE(flipped, b); - + // Test color conversion if (b.getChannels() == 3) { blob gray = b; @@ -375,7 +375,7 @@ TEST_F(BlobTest, OpenCVIntegration) { TEST_F(BlobTest, OpenCVImageIO) { // Create a test matrix cv::Mat mat(2, 2, CV_8UC3); - + // Fill with test data for (int i = 0; i < 2; ++i) { for (int j = 0; j < 2; ++j) { @@ -384,21 +384,21 @@ TEST_F(BlobTest, OpenCVImageIO) { } } } - + // Create blob from matrix blob b(mat); - + // Save to file b.save(test_image_path); - + // Load from file blob loaded = blob::load(test_image_path); - + // Size and channels should be the same EXPECT_EQ(loaded.getRows(), b.getRows()); EXPECT_EQ(loaded.getCols(), b.getCols()); EXPECT_EQ(loaded.getChannels(), b.getChannels()); - + // Test loading non-existent file EXPECT_THROW(blob::load("non_existent_file.png"), std::runtime_error); } @@ -409,7 +409,7 @@ TEST_F(BlobTest, OpenCVImageIO) { TEST_F(BlobTest, CImgIntegration) { // Create a CImg cimg_library::CImg img(2, 2, 1, 3); - + // Fill with test data for (int y = 0; y < 2; ++y) { for (int x = 0; x < 2; ++x) { @@ -418,18 +418,18 @@ TEST_F(BlobTest, CImgIntegration) { } } } - + // Create blob from CImg blob b(img); - + EXPECT_EQ(b.getRows(), 2); EXPECT_EQ(b.getCols(), 2); EXPECT_EQ(b.getChannels(), 3); EXPECT_EQ(b.size(), 12); - + // Convert back to CImg cimg_library::CImg reconstructed = b.to_cimg(); - + // Verify image equality for (int y = 0; y < 2; ++y) { for (int x = 0; x < 2; ++x) { @@ -438,14 +438,14 @@ TEST_F(BlobTest, CImgIntegration) { } } } - + // Test filter application cimg_library::CImg kernel(3, 3, 1, 1, 0); kernel(1, 1) = 1.0f; // Identity filter - + blob filtered = b; filtered.apply_cimg_filter(kernel); - + // Should be similar to original after applying identity filter EXPECT_EQ(filtered.getRows(), b.getRows()); EXPECT_EQ(filtered.getCols(), b.getCols()); @@ -490,7 +490,7 @@ TEST_F(BlobTest, StbImageIntegration) { 0x00, 0x00, 0x00, 0x00 // Important colors }; fwrite(bmp_header, sizeof(bmp_header), 1, f); - + // Write test data (BGR order for BMP) for (int i = 0; i < test_data.size(); i += 3) { unsigned char bgr[3] = { @@ -503,27 +503,27 @@ TEST_F(BlobTest, StbImageIntegration) { fclose(f); } #endif - + // Load with stb_image blob b(test_image_path); - + // Basic checks EXPECT_EQ(b.getCols(), 2); EXPECT_EQ(b.getRows(), 2); EXPECT_EQ(b.getChannels(), 3); - + // Save with different formats b.save_as(test_image_path + ".png", "png"); b.save_as(test_image_path + ".bmp", "bmp"); b.save_as(test_image_path + ".jpg", "jpg"); b.save_as(test_image_path + ".tga", "tga"); - + // Clean up std::remove((test_image_path + ".png").c_str()); std::remove((test_image_path + ".bmp").c_str()); std::remove((test_image_path + ".jpg").c_str()); std::remove((test_image_path + ".tga").c_str()); - + // Test invalid format EXPECT_THROW(b.save_as(test_image_path + ".invalid", "invalid"), std::runtime_error); } @@ -534,24 +534,24 @@ TEST_F(BlobTest, FastModeLimitations) { // Create a fast blob std::vector data(test_data); fast_blob fb(data.data(), data.size()); - + // These operations should throw in FAST mode EXPECT_THROW(fb.append(fb), std::runtime_error); EXPECT_THROW(fb.append(data.data(), data.size()), std::runtime_error); EXPECT_THROW(fb.allocate(20), std::runtime_error); EXPECT_THROW(fb.deallocate(), std::runtime_error); - + #if __has_include() // CImg operations should throw in FAST mode cimg_library::CImg kernel(3, 3); EXPECT_THROW(fb.apply_cimg_filter(kernel), std::runtime_error); EXPECT_THROW(fb.to_cimg(), std::runtime_error); #endif - + #if __has_include() // stb_image operations should throw in FAST mode EXPECT_THROW(fb.save_as(test_image_path, "png"), std::runtime_error); - + // Fast mode constructor from stb_image should throw EXPECT_THROW(fast_blob bad_fb(test_image_path), std::runtime_error); #endif @@ -562,4 +562,4 @@ TEST_F(BlobTest, FastModeLimitations) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/io/test_async_compress.cpp b/tests/io/test_async_compress.cpp index 81aafdc2..bb8911b2 100644 --- a/tests/io/test_async_compress.cpp +++ b/tests/io/test_async_compress.cpp @@ -25,82 +25,82 @@ class AsyncCompressTest : public ::testing::Test { test_dir_ = fs::temp_directory_path() / "atom_compress_test"; input_dir_ = test_dir_ / "input"; output_dir_ = test_dir_ / "output"; - + // Clean up any existing test directories if (fs::exists(test_dir_)) { fs::remove_all(test_dir_); } - + // Create fresh directories fs::create_directories(input_dir_); fs::create_directories(output_dir_); - + // Create test files with content createTestFile(input_dir_ / "test1.txt", "This is test file 1 content."); createTestFile(input_dir_ / "test2.txt", "This is test file 2 with different content."); createTestFile(input_dir_ / "test3.txt", std::string(50000, 'x')); // Larger file - + // Create a subdirectory with files fs::create_directories(input_dir_ / "subdir"); createTestFile(input_dir_ / "subdir" / "subfile1.txt", "Subdirectory file content."); - + // Set up io_context and work guard to keep io_context running work_guard_ = std::make_unique>( io_context_.get_executor()); - + // Start io_context in a separate thread io_thread_ = std::thread([this]() { io_context_.run(); }); } - + void TearDown() override { // Allow io_context to finish and join thread work_guard_.reset(); if (io_thread_.joinable()) { io_thread_.join(); } - + // Clean up test directory if (fs::exists(test_dir_)) { fs::remove_all(test_dir_); } } - + void createTestFile(const fs::path& path, const std::string& content) { std::ofstream file(path); file << content; file.close(); ASSERT_TRUE(fs::exists(path)) << "Failed to create test file: " << path; } - + bool fileContentsEqual(const fs::path& file1, const fs::path& file2) { std::ifstream f1(file1, std::ios::binary); std::ifstream f2(file2, std::ios::binary); - + if (!f1.is_open() || !f2.is_open()) { return false; } - + constexpr size_t BUFFER_SIZE = 4096; std::array buffer1, buffer2; - + while (f1 && f2) { f1.read(buffer1.data(), buffer1.size()); f2.read(buffer2.data(), buffer2.size()); - + if (f1.gcount() != f2.gcount()) { return false; } - + if (std::memcmp(buffer1.data(), buffer2.data(), f1.gcount()) != 0) { return false; } } - + return f1.eof() && f2.eof(); } - + // Wait for an operation to complete void waitForCompletion(std::chrono::milliseconds timeout = std::chrono::seconds(5)) { std::unique_lock lock(completion_mutex_); @@ -108,7 +108,7 @@ class AsyncCompressTest : public ::testing::Test { << "Operation timed out"; operation_completed_ = false; } - + void signalCompletion() { { std::lock_guard lock(completion_mutex_); @@ -116,15 +116,15 @@ class AsyncCompressTest : public ::testing::Test { } completion_cv_.notify_one(); } - + asio::io_context io_context_; std::unique_ptr> work_guard_; std::thread io_thread_; - + fs::path test_dir_; fs::path input_dir_; fs::path output_dir_; - + std::mutex completion_mutex_; std::condition_variable completion_cv_; bool operation_completed_ = false; @@ -134,23 +134,23 @@ class AsyncCompressTest : public ::testing::Test { TEST_F(AsyncCompressTest, SingleFileCompressorBasicOperation) { fs::path input_file = input_dir_ / "test1.txt"; fs::path output_file = output_dir_ / "test1.txt.gz"; - + // Create a completion handler auto handler = [this](const asio::error_code& ec, std::size_t bytes_transferred) { EXPECT_FALSE(ec) << "Error in async operation: " << ec.message(); signalCompletion(); }; - + // Create and start the compressor auto compressor = std::make_shared( io_context_, input_file, output_file); - + // Hook into the completion using a lambda that captures our handler compressor->start(); - + // Wait for operation to complete waitForCompletion(); - + // Verify output file exists and is not empty ASSERT_TRUE(fs::exists(output_file)) << "Output file was not created"; EXPECT_GT(fs::file_size(output_file), 0) << "Output file is empty"; @@ -159,23 +159,23 @@ TEST_F(AsyncCompressTest, SingleFileCompressorBasicOperation) { // Test DirectoryCompressor functionality TEST_F(AsyncCompressTest, DirectoryCompressorBasicOperation) { fs::path output_file = output_dir_ / "all_files.gz"; - + // Create a completion handler auto handler = [this](const asio::error_code& ec, std::size_t bytes_transferred) { EXPECT_FALSE(ec) << "Error in async operation: " << ec.message(); signalCompletion(); }; - + // Create and start the compressor auto compressor = std::make_shared( io_context_, input_dir_, output_file); - + // Start compression compressor->start(); - + // Wait for operation to complete waitForCompletion(); - + // Verify output file exists and is not empty ASSERT_TRUE(fs::exists(output_file)) << "Output file was not created"; EXPECT_GT(fs::file_size(output_file), 0) << "Output file is empty"; @@ -186,30 +186,30 @@ TEST_F(AsyncCompressTest, SingleFileDecompressorBasicOperation) { // First compress a file fs::path input_file = input_dir_ / "test1.txt"; fs::path compressed_file = output_dir_ / "test1.txt.gz"; - + { auto compressor = std::make_shared( io_context_, input_file, compressed_file); compressor->start(); waitForCompletion(); } - + // Now decompress it fs::path decompressed_file = output_dir_ / "decompressed_test1.txt"; - + auto decompressor = std::make_shared( io_context_, compressed_file, output_dir_); - + // Start decompression decompressor->start(); - + // Wait for operation to complete waitForCompletion(); - + // Verify decompressed file exists and content matches original - ASSERT_TRUE(fs::exists(output_dir_ / "test1.txt")) + ASSERT_TRUE(fs::exists(output_dir_ / "test1.txt")) << "Decompressed file was not created"; - + EXPECT_TRUE(fileContentsEqual(input_file, output_dir_ / "test1.txt")) << "Decompressed content does not match original"; } @@ -218,28 +218,28 @@ TEST_F(AsyncCompressTest, SingleFileDecompressorBasicOperation) { TEST_F(AsyncCompressTest, DirectoryDecompressorBasicOperation) { // First compress the directory fs::path compressed_file = output_dir_ / "all_files.gz"; - + { auto compressor = std::make_shared( io_context_, input_dir_, compressed_file); compressor->start(); waitForCompletion(); } - + // Create a new output directory for decompressed files fs::path decompressed_dir = output_dir_ / "decompressed"; fs::create_directories(decompressed_dir); - + // Now decompress it auto decompressor = std::make_shared( io_context_, output_dir_, decompressed_dir); - + // Start decompression decompressor->start(); - + // Wait for operation to complete waitForCompletion(); - + // Verify at least one decompressed file exists bool found_decompressed_file = false; for (const auto& entry : fs::directory_iterator(decompressed_dir)) { @@ -248,7 +248,7 @@ TEST_F(AsyncCompressTest, DirectoryDecompressorBasicOperation) { break; } } - + EXPECT_TRUE(found_decompressed_file) << "No decompressed files were created"; } @@ -256,7 +256,7 @@ TEST_F(AsyncCompressTest, DirectoryDecompressorBasicOperation) { TEST_F(AsyncCompressTest, CompressorErrorHandlingNonExistentFile) { fs::path non_existent_file = input_dir_ / "does_not_exist.txt"; fs::path output_file = output_dir_ / "error_output.gz"; - + // Expect an exception when trying to compress a non-existent file EXPECT_THROW({ auto compressor = std::make_shared( @@ -269,7 +269,7 @@ TEST_F(AsyncCompressTest, CompressorErrorHandlingNonExistentFile) { TEST_F(AsyncCompressTest, CompressorErrorHandlingInvalidOutputPath) { fs::path input_file = input_dir_ / "test1.txt"; fs::path invalid_output_file = fs::path("/non_existent_dir") / "output.gz"; - + // Expect an exception when trying to write to an invalid path EXPECT_THROW({ auto compressor = std::make_shared( @@ -282,86 +282,86 @@ TEST_F(AsyncCompressTest, CompressorErrorHandlingInvalidOutputPath) { TEST_F(AsyncCompressTest, ZipOperations) { // Create a test ZIP file fs::path zip_file = output_dir_ / "test.zip"; - + // We need to check if zip is available bool zip_available = atom::system::checkSoftwareInstalled("zip"); if (!zip_available) { GTEST_SKIP() << "Skipping test as 'zip' command is not available"; } - + // Create a ZIP file for testing using system commands - std::string cmd = "zip -j " + zip_file.string() + " " + + std::string cmd = "zip -j " + zip_file.string() + " " + (input_dir_ / "test1.txt").string() + " " + (input_dir_ / "test2.txt").string(); int result = std::system(cmd.c_str()); ASSERT_EQ(result, 0) << "Failed to create test ZIP file"; - + // Test ListFilesInZip { auto list_files = std::make_shared(io_context_, zip_file.string()); list_files->start(); - + // Wait for io operations to complete std::this_thread::sleep_for(std::chrono::milliseconds(500)); - + auto file_list = list_files->getFileList(); EXPECT_EQ(file_list.size(), 2); EXPECT_THAT(file_list, Contains(HasSubstr("test1.txt"))); EXPECT_THAT(file_list, Contains(HasSubstr("test2.txt"))); } - + // Test FileExistsInZip { auto file_exists = std::make_shared( io_context_, zip_file.string(), "test1.txt"); file_exists->start(); - + // Wait for io operations to complete std::this_thread::sleep_for(std::chrono::milliseconds(500)); - + EXPECT_TRUE(file_exists->found()); - + auto file_not_exists = std::make_shared( io_context_, zip_file.string(), "non_existent.txt"); file_not_exists->start(); - + // Wait for io operations to complete std::this_thread::sleep_for(std::chrono::milliseconds(500)); - + EXPECT_FALSE(file_not_exists->found()); } - + // Test GetZipFileSize { auto get_size = std::make_shared(io_context_, zip_file.string()); get_size->start(); - + // Wait for io operations to complete std::this_thread::sleep_for(std::chrono::milliseconds(500)); - + EXPECT_GT(get_size->getSizeValue(), 0); } - + // Test RemoveFileFromZip { auto remove_file = std::make_shared( io_context_, zip_file.string(), "test1.txt"); remove_file->start(); - + // Wait for io operations to complete std::this_thread::sleep_for(std::chrono::milliseconds(500)); - + // Check if removal was successful EXPECT_TRUE(remove_file->isSuccessful()); - + // Verify the file is no longer in the ZIP auto file_exists = std::make_shared( io_context_, zip_file.string(), "test1.txt"); file_exists->start(); - + // Wait for io operations to complete std::this_thread::sleep_for(std::chrono::milliseconds(500)); - + EXPECT_FALSE(file_exists->found()); } } @@ -372,11 +372,11 @@ TEST_F(AsyncCompressTest, ConcurrentCompression) { fs::path input_file1 = input_dir_ / "test1.txt"; fs::path input_file2 = input_dir_ / "test2.txt"; fs::path input_file3 = input_dir_ / "test3.txt"; - + fs::path output_file1 = output_dir_ / "test1.txt.gz"; fs::path output_file2 = output_dir_ / "test2.txt.gz"; fs::path output_file3 = output_dir_ / "test3.txt.gz"; - + // Create compressors auto compressor1 = std::make_shared( io_context_, input_file1, output_file1); @@ -384,20 +384,20 @@ TEST_F(AsyncCompressTest, ConcurrentCompression) { io_context_, input_file2, output_file2); auto compressor3 = std::make_shared( io_context_, input_file3, output_file3); - + // Start compressions concurrently compressor1->start(); compressor2->start(); compressor3->start(); - + // Wait for a reasonable amount of time for all operations to complete std::this_thread::sleep_for(std::chrono::seconds(3)); - + // Verify all output files exist EXPECT_TRUE(fs::exists(output_file1)) << "Output file 1 was not created"; EXPECT_TRUE(fs::exists(output_file2)) << "Output file 2 was not created"; EXPECT_TRUE(fs::exists(output_file3)) << "Output file 3 was not created"; - + // Verify all output files are not empty EXPECT_GT(fs::file_size(output_file1), 0) << "Output file 1 is empty"; EXPECT_GT(fs::file_size(output_file2), 0) << "Output file 2 is empty"; @@ -412,13 +412,13 @@ TEST_F(AsyncCompressTest, CompressDecompressRoundTrip) { input_dir_ / "test2.txt", input_dir_ / "test3.txt" }; - + // Create separate output directories for each file for (size_t i = 0; i < input_files.size(); ++i) { fs::path compressed_file = output_dir_ / (std::to_string(i) + ".gz"); fs::path decomp_dir = output_dir_ / ("decomp_" + std::to_string(i)); fs::create_directories(decomp_dir); - + // Compress { auto compressor = std::make_shared( @@ -426,7 +426,7 @@ TEST_F(AsyncCompressTest, CompressDecompressRoundTrip) { compressor->start(); std::this_thread::sleep_for(std::chrono::milliseconds(500)); } - + // Decompress { auto decompressor = std::make_shared( @@ -434,13 +434,13 @@ TEST_F(AsyncCompressTest, CompressDecompressRoundTrip) { decompressor->start(); std::this_thread::sleep_for(std::chrono::milliseconds(500)); } - + // Get the original filename fs::path original_name = input_files[i].filename(); - + // Verify content matches EXPECT_TRUE(fileContentsEqual( - input_files[i], + input_files[i], decomp_dir / original_name )) << "Round-trip content does not match for file " << i; } @@ -450,7 +450,7 @@ TEST_F(AsyncCompressTest, CompressDecompressRoundTrip) { TEST_F(AsyncCompressTest, CompressionPerformance) { // This test would typically measure and compare compression times and ratios // For different files or compression settings - + // Create a large test file fs::path large_file = input_dir_ / "large_file.txt"; { @@ -460,36 +460,36 @@ TEST_F(AsyncCompressTest, CompressionPerformance) { file << std::string(1024, 'a' + (i % 26)); } } - + fs::path output_file = output_dir_ / "large_file.gz"; - + // Record start time auto start_time = std::chrono::high_resolution_clock::now(); - + // Compress the file auto compressor = std::make_shared( io_context_, large_file, output_file); compressor->start(); - + // Wait for completion waitForCompletion(); - + // Record end time auto end_time = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast( end_time - start_time).count(); - + // Calculate compression ratio double original_size = static_cast(fs::file_size(large_file)); double compressed_size = static_cast(fs::file_size(output_file)); double compression_ratio = original_size / compressed_size; - + // Log performance metrics std::cout << "Compression time: " << duration << "ms\n"; std::cout << "Original size: " << original_size << " bytes\n"; std::cout << "Compressed size: " << compressed_size << " bytes\n"; std::cout << "Compression ratio: " << compression_ratio << ":1\n"; - + // Expect reasonable compression ratio for our test data EXPECT_GT(compression_ratio, 2.0) << "Compression ratio is lower than expected"; } @@ -497,4 +497,4 @@ TEST_F(AsyncCompressTest, CompressionPerformance) { int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/io/test_async_glob.cpp b/tests/io/test_async_glob.cpp index 90f87dae..bae73989 100644 --- a/tests/io/test_async_glob.cpp +++ b/tests/io/test_async_glob.cpp @@ -25,12 +25,12 @@ class AsyncGlobTest : public ::testing::Test { void SetUp() override { // Create a temporary test directory structure testDir = fs::temp_directory_path() / "async_glob_test"; - + // Clean up any existing test directory if (fs::exists(testDir)) { fs::remove_all(testDir); } - + fs::create_directory(testDir); fs::create_directory(testDir / "dir1"); fs::create_directory(testDir / "dir2"); @@ -38,7 +38,7 @@ class AsyncGlobTest : public ::testing::Test { fs::create_directory(testDir / "dir1" / "subdir2"); fs::create_directory(testDir / "dir2" / "subdir1"); fs::create_directory(testDir / ".hidden_dir"); - + // Create some test files createFile(testDir / "file1.txt", "Test file 1"); createFile(testDir / "file2.txt", "Test file 2"); @@ -48,33 +48,33 @@ class AsyncGlobTest : public ::testing::Test { createFile(testDir / "dir2" / "file1.log", "Test file in dir2"); createFile(testDir / "dir1" / "subdir1" / "nested.txt", "Nested file"); createFile(testDir / ".hidden_file.txt", "Hidden file"); - + // Initialize IO context io_context = std::make_unique(); } - + void TearDown() override { // Clean up the test directory if (fs::exists(testDir)) { fs::remove_all(testDir); } - + // Make sure IO context is stopped io_context->stop(); } - + void createFile(const fs::path& path, const std::string& content) { std::ofstream file(path); file << content; file.close(); } - + // Helper to run the io_context void runContext() { io_context->run_for(std::chrono::milliseconds(100)); io_context->restart(); } - + fs::path testDir; std::unique_ptr io_context; }; @@ -87,9 +87,9 @@ TEST_F(AsyncGlobTest, Constructor) { // Test glob_sync with simple pattern TEST_F(AsyncGlobTest, GlobSyncSimplePattern) { AsyncGlob glob(*io_context); - + auto result = glob.glob_sync((testDir / "*.txt").string()); - + ASSERT_EQ(result.size(), 2); EXPECT_THAT(result, Contains(testDir / "file1.txt")); EXPECT_THAT(result, Contains(testDir / "file2.txt")); @@ -98,9 +98,9 @@ TEST_F(AsyncGlobTest, GlobSyncSimplePattern) { // Test glob_sync with directory pattern TEST_F(AsyncGlobTest, GlobSyncDirectoryPattern) { AsyncGlob glob(*io_context); - + auto result = glob.glob_sync((testDir / "dir*").string(), false, true); - + ASSERT_EQ(result.size(), 2); EXPECT_THAT(result, Contains(testDir / "dir1")); EXPECT_THAT(result, Contains(testDir / "dir2")); @@ -109,9 +109,9 @@ TEST_F(AsyncGlobTest, GlobSyncDirectoryPattern) { // Test glob_sync with recursive search TEST_F(AsyncGlobTest, GlobSyncRecursive) { AsyncGlob glob(*io_context); - + auto result = glob.glob_sync((testDir).string(), true, false); - + // Should find all non-hidden files and directories EXPECT_GT(result.size(), 10); EXPECT_THAT(result, Contains(testDir / "file1.txt")); @@ -124,19 +124,19 @@ TEST_F(AsyncGlobTest, GlobWithCallback) { std::vector callbackResult; std::promise callbackPromise; auto callbackFuture = callbackPromise.get_future(); - - glob.glob((testDir / "*.txt").string(), + + glob.glob((testDir / "*.txt").string(), [&callbackResult, &callbackPromise](std::vector result) { callbackResult = std::move(result); callbackPromise.set_value(); }); - + runContext(); - + // Wait for the callback to be called - ASSERT_EQ(callbackFuture.wait_for(std::chrono::seconds(1)), + ASSERT_EQ(callbackFuture.wait_for(std::chrono::seconds(1)), std::future_status::ready); - + ASSERT_EQ(callbackResult.size(), 2); EXPECT_THAT(callbackResult, Contains(testDir / "file1.txt")); EXPECT_THAT(callbackResult, Contains(testDir / "file2.txt")); @@ -145,12 +145,12 @@ TEST_F(AsyncGlobTest, GlobWithCallback) { // Test glob_async with coroutine TEST_F(AsyncGlobTest, GlobAsync) { AsyncGlob glob(*io_context); - + auto task = glob.glob_async((testDir / "*.txt").string()); auto result = task.get_result(); - + runContext(); - + ASSERT_EQ(result.size(), 2); EXPECT_THAT(result, Contains(testDir / "file1.txt")); EXPECT_THAT(result, Contains(testDir / "file2.txt")); @@ -159,9 +159,9 @@ TEST_F(AsyncGlobTest, GlobAsync) { // Test with complex pattern TEST_F(AsyncGlobTest, ComplexPattern) { AsyncGlob glob(*io_context); - + auto result = glob.glob_sync((testDir / "dir1" / "*" / "*.txt").string()); - + ASSERT_EQ(result.size(), 1); EXPECT_THAT(result, Contains(testDir / "dir1" / "subdir1" / "nested.txt")); } @@ -169,9 +169,9 @@ TEST_F(AsyncGlobTest, ComplexPattern) { // Test with question mark wildcard TEST_F(AsyncGlobTest, QuestionMarkWildcard) { AsyncGlob glob(*io_context); - + auto result = glob.glob_sync((testDir / "file?.txt").string()); - + ASSERT_EQ(result.size(), 2); EXPECT_THAT(result, Contains(testDir / "file1.txt")); EXPECT_THAT(result, Contains(testDir / "file2.txt")); @@ -180,9 +180,9 @@ TEST_F(AsyncGlobTest, QuestionMarkWildcard) { // Test with character class wildcard TEST_F(AsyncGlobTest, CharacterClassWildcard) { AsyncGlob glob(*io_context); - + auto result = glob.glob_sync((testDir / "file[1-2].txt").string()); - + ASSERT_EQ(result.size(), 2); EXPECT_THAT(result, Contains(testDir / "file1.txt")); EXPECT_THAT(result, Contains(testDir / "file2.txt")); @@ -191,9 +191,9 @@ TEST_F(AsyncGlobTest, CharacterClassWildcard) { // Test with negated character class TEST_F(AsyncGlobTest, NegatedCharacterClass) { AsyncGlob glob(*io_context); - + auto result = glob.glob_sync((testDir / "file[!3].txt").string()); - + ASSERT_EQ(result.size(), 2); EXPECT_THAT(result, Contains(testDir / "file1.txt")); EXPECT_THAT(result, Contains(testDir / "file2.txt")); @@ -202,9 +202,9 @@ TEST_F(AsyncGlobTest, NegatedCharacterClass) { // Test with recursive pattern TEST_F(AsyncGlobTest, RecursivePattern) { AsyncGlob glob(*io_context); - + auto result = glob.glob_sync((testDir / "**" / "*.txt").string()); - + EXPECT_GT(result.size(), 3); EXPECT_THAT(result, Contains(testDir / "file1.txt")); EXPECT_THAT(result, Contains(testDir / "file2.txt")); @@ -215,9 +215,9 @@ TEST_F(AsyncGlobTest, RecursivePattern) { // Test with non-existent directory TEST_F(AsyncGlobTest, NonExistentDirectory) { AsyncGlob glob(*io_context); - + auto result = glob.glob_sync((testDir / "non_existent_dir" / "*.txt").string()); - + EXPECT_TRUE(result.empty()); } @@ -225,20 +225,20 @@ TEST_F(AsyncGlobTest, NonExistentDirectory) { TEST_F(AsyncGlobTest, EmptyDirectory) { // Create an empty directory fs::create_directory(testDir / "empty_dir"); - + AsyncGlob glob(*io_context); - + auto result = glob.glob_sync((testDir / "empty_dir" / "*.txt").string()); - + EXPECT_TRUE(result.empty()); } // Test with dir-only flag TEST_F(AsyncGlobTest, DirOnlyFlag) { AsyncGlob glob(*io_context); - + auto result = glob.glob_sync((testDir / "*").string(), false, true); - + // Should only match directories, not files for (const auto& path : result) { EXPECT_TRUE(fs::is_directory(path)); @@ -250,9 +250,9 @@ TEST_F(AsyncGlobTest, DirOnlyFlag) { // Test with hidden files TEST_F(AsyncGlobTest, HiddenFiles) { AsyncGlob glob(*io_context); - + auto result = glob.glob_sync((testDir / ".*").string()); - + // Should find hidden files/directories EXPECT_THAT(result, Contains(testDir / ".hidden_file.txt")); EXPECT_THAT(result, Contains(testDir / ".hidden_dir")); @@ -262,7 +262,7 @@ TEST_F(AsyncGlobTest, HiddenFiles) { TEST_F(AsyncGlobTest, TildeExpansion) { // This test is platform-dependent, so we'll make a conditional test AsyncGlob glob(*io_context); - + // Just verify it doesn't throw - actual expansion is platform-dependent EXPECT_NO_THROW(glob.glob_sync("~/test_pattern")); } @@ -271,14 +271,14 @@ TEST_F(AsyncGlobTest, TildeExpansion) { TEST_F(AsyncGlobTest, ParallelGlob) { AsyncGlob glob(*io_context); std::vector>> futures; - + // Start multiple glob operations in parallel for (int i = 0; i < 5; i++) { futures.push_back(std::async(std::launch::async, [&glob, this]() { return glob.glob_sync((testDir / "*.txt").string()); })); } - + // Check results from all operations for (auto& future : futures) { auto result = future.get(); @@ -291,10 +291,10 @@ TEST_F(AsyncGlobTest, ParallelGlob) { // Test error handling with invalid pattern TEST_F(AsyncGlobTest, InvalidPattern) { AsyncGlob glob(*io_context); - + // Unbalanced bracket should be handled gracefully auto result = glob.glob_sync((testDir / "file[1.txt").string()); - + // Should either be empty or return a valid subset of files if (!result.empty()) { for (const auto& path : result) { @@ -306,9 +306,9 @@ TEST_F(AsyncGlobTest, InvalidPattern) { // Test with pattern ending in directory separator TEST_F(AsyncGlobTest, PatternEndingInSeparator) { AsyncGlob glob(*io_context); - + auto result = glob.glob_sync((testDir / "dir1/").string(), false, true); - + // Should match the directory ASSERT_EQ(result.size(), 1); EXPECT_THAT(result, Contains(testDir / "dir1")); @@ -317,24 +317,24 @@ TEST_F(AsyncGlobTest, PatternEndingInSeparator) { // Test with absolute and relative paths TEST_F(AsyncGlobTest, AbsoluteVsRelativePaths) { AsyncGlob glob(*io_context); - + // Change to the test directory auto originalPath = fs::current_path(); fs::current_path(testDir); - + // Do a relative path glob auto relativeResult = glob.glob_sync("*.txt"); - + // Change back to original directory fs::current_path(originalPath); - + // Do an absolute path glob auto absoluteResult = glob.glob_sync((testDir / "*.txt").string()); - + // The number of results should be the same ASSERT_EQ(relativeResult.size(), absoluteResult.size()); ASSERT_EQ(relativeResult.size(), 2); - + // But the paths will be different (relative vs absolute) EXPECT_THAT(relativeResult, Contains(fs::path("file1.txt"))); EXPECT_THAT(relativeResult, Contains(fs::path("file2.txt"))); @@ -347,21 +347,21 @@ TEST_F(AsyncGlobTest, DeepDirectoryStructure) { // Create a deep directory structure fs::path deepDir = testDir / "deep"; fs::create_directory(deepDir); - + fs::path currentPath = deepDir; for (int i = 0; i < 20; i++) { currentPath = currentPath / ("level" + std::to_string(i)); fs::create_directory(currentPath); } - + // Create a file at the deepest level createFile(currentPath / "deep_file.txt", "Deep file"); - + AsyncGlob glob(*io_context); - + // Test recursive glob on deep structure auto result = glob.glob_sync((deepDir / "**" / "*.txt").string()); - + ASSERT_EQ(result.size(), 1); EXPECT_THAT(result, Contains(currentPath / "deep_file.txt")); } @@ -371,26 +371,26 @@ TEST_F(AsyncGlobTest, PerformanceWithManyFiles) { // Create directory with many files fs::path manyFilesDir = testDir / "many_files"; fs::create_directory(manyFilesDir); - + const int numFiles = 100; // Can increase for more thorough testing for (int i = 0; i < numFiles; i++) { - createFile(manyFilesDir / ("file" + std::to_string(i) + ".txt"), + createFile(manyFilesDir / ("file" + std::to_string(i) + ".txt"), "Content " + std::to_string(i)); } - + AsyncGlob glob(*io_context); - + auto start = std::chrono::high_resolution_clock::now(); auto result = glob.glob_sync((manyFilesDir / "*.txt").string()); auto end = std::chrono::high_resolution_clock::now(); - + auto duration = std::chrono::duration_cast(end - start).count(); - + ASSERT_EQ(result.size(), numFiles); - + // Performance check - should be reasonably fast std::cout << "Time to glob " << numFiles << " files: " << duration << "ms" << std::endl; - + // This is not a strict test as timing depends on the system, // but we can output the timing for information } @@ -400,39 +400,39 @@ TEST_F(AsyncGlobTest, ConcurrentModification) { // Create a directory for the test fs::path concurrentDir = testDir / "concurrent"; fs::create_directory(concurrentDir); - + // Add some initial files createFile(concurrentDir / "file1.txt", "Initial file 1"); createFile(concurrentDir / "file2.txt", "Initial file 2"); - + AsyncGlob glob(*io_context); - + // Start a glob operation in a separate thread std::promise> resultPromise; auto resultFuture = resultPromise.get_future(); - + std::thread globThread([&glob, &concurrentDir, &resultPromise]() { // Simulate a slow glob operation auto result = glob.glob_sync((concurrentDir / "*.txt").string()); std::this_thread::sleep_for(std::chrono::milliseconds(50)); resultPromise.set_value(result); }); - + // Modify the directory while the glob is running std::this_thread::sleep_for(std::chrono::milliseconds(10)); createFile(concurrentDir / "file3.txt", "Added during glob"); fs::remove(concurrentDir / "file1.txt"); - + // Wait for the glob to complete auto result = resultFuture.get(); globThread.join(); - + // We can't make strict assertions about what should be returned, as it depends // on timing, but we can verify it didn't crash and returned something reasonable for (const auto& path : result) { std::cout << "Found in concurrent test: " << path << std::endl; } - + // Verify the final state auto finalResult = glob.glob_sync((concurrentDir / "*.txt").string()); ASSERT_EQ(finalResult.size(), 2); @@ -448,19 +448,19 @@ TEST_F(AsyncGlobTest, SpecialCharacters) { createFile(testDir / "file-with-dashes.txt", "Dash file"); createFile(testDir / "file+with+plus.txt", "Plus file"); createFile(testDir / "file.with.dots.txt", "Dot file"); - + AsyncGlob glob(*io_context); - + // Test glob with space in pattern auto spaceResult = glob.glob_sync((testDir / "file with*.txt").string()); ASSERT_EQ(spaceResult.size(), 1); EXPECT_THAT(spaceResult, Contains(testDir / "file with spaces.txt")); - + // Test glob with bracket in pattern (requires escaping) auto bracketResult = glob.glob_sync((testDir / "file_with_\\[*").string()); ASSERT_EQ(bracketResult.size(), 1); EXPECT_THAT(bracketResult, Contains(testDir / "file_with_[brackets].txt")); - + // Test glob with various special characters auto mixedResult = glob.glob_sync((testDir / "file*").string()); ASSERT_EQ(mixedResult.size(), 8); // Includes the original files @@ -472,4 +472,4 @@ TEST_F(AsyncGlobTest, SpecialCharacters) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/io/test_async_io.cpp b/tests/io/test_async_io.cpp index 558baa18..69ad181d 100644 --- a/tests/io/test_async_io.cpp +++ b/tests/io/test_async_io.cpp @@ -22,71 +22,71 @@ class AsyncIOTest : public ::testing::Test { void SetUp() override { // Create a temporary test directory structure testDir = fs::temp_directory_path() / "async_io_test"; - + // Clean up any existing test directory if (fs::exists(testDir)) { fs::remove_all(testDir); } - + fs::create_directory(testDir); - + // Create test files createFile(testDir / "file1.txt", "Test file 1 content"); createFile(testDir / "file2.txt", "Test file 2 content\nwith multiple lines"); createFile(testDir / "file3.dat", "Binary file content\0with null bytes", 35); - + // Create subdirectories fs::create_directory(testDir / "subdir1"); fs::create_directory(testDir / "subdir2"); - + createFile(testDir / "subdir1" / "nested_file.txt", "Nested file content"); - + // Initialize IO context and start thread to run it io_context_ptr = std::make_unique(); - + // Start the io_context in a separate thread io_thread = std::thread([this]() { asio::io_context::work work(*io_context_ptr); io_context_ptr->run(); }); - + // Create the async file instance async_file = std::make_unique(*io_context_ptr); async_dir = std::make_unique(*io_context_ptr); } - + void TearDown() override { // Stop the io_context and join the thread io_context_ptr->stop(); if (io_thread.joinable()) { io_thread.join(); } - + // Clean up the test directory if (fs::exists(testDir)) { fs::remove_all(testDir); } } - + void createFile(const fs::path& path, const std::string& content) { std::ofstream file(path); file << content; file.close(); } - + void createFile(const fs::path& path, const char* content, size_t size) { std::ofstream file(path, std::ios::binary); file.write(content, size); file.close(); } - + // Helper for waiting on futures with timeout template bool waitForFuture(std::future& future, int timeoutMs = 1000) { - return future.wait_for(std::chrono::milliseconds(timeoutMs)) == + return future.wait_for(std::chrono::milliseconds(timeoutMs)) == std::future_status::ready; } - + fs::path testDir; std::unique_ptr io_context_ptr; std::thread io_thread; @@ -108,15 +108,15 @@ TEST_F(AsyncIOTest, AsyncDirectoryConstructor) { TEST_F(AsyncIOTest, AsyncFileReadExistingFile) { std::promise> promise; auto future = promise.get_future(); - - async_file->asyncRead(testDir / "file1.txt", + + async_file->asyncRead(testDir / "file1.txt", [&promise](AsyncResult result) { promise.set_value(std::move(result)); }); - + ASSERT_TRUE(waitForFuture(future)); auto result = future.get(); - + EXPECT_TRUE(result.success); EXPECT_EQ(result.value, "Test file 1 content"); EXPECT_TRUE(result.error_message.empty()); @@ -126,15 +126,15 @@ TEST_F(AsyncIOTest, AsyncFileReadExistingFile) { TEST_F(AsyncIOTest, AsyncFileReadNonExistentFile) { std::promise> promise; auto future = promise.get_future(); - - async_file->asyncRead(testDir / "non_existent.txt", + + async_file->asyncRead(testDir / "non_existent.txt", [&promise](AsyncResult result) { promise.set_value(std::move(result)); }); - + ASSERT_TRUE(waitForFuture(future)); auto result = future.get(); - + EXPECT_FALSE(result.success); EXPECT_THAT(result.error_message, HasSubstr("does not exist")); } @@ -143,25 +143,25 @@ TEST_F(AsyncIOTest, AsyncFileReadNonExistentFile) { TEST_F(AsyncIOTest, AsyncFileWriteNewFile) { std::promise> promise; auto future = promise.get_future(); - + std::string content = "New file content"; fs::path newFilePath = testDir / "new_file.txt"; - - async_file->asyncWrite(newFilePath, std::span(content.data(), content.size()), + + async_file->asyncWrite(newFilePath, std::span(content.data(), content.size()), [&promise](AsyncResult result) { promise.set_value(std::move(result)); }); - + ASSERT_TRUE(waitForFuture(future)); auto result = future.get(); - + EXPECT_TRUE(result.success); EXPECT_TRUE(result.error_message.empty()); - + // Verify file was created with correct content EXPECT_TRUE(fs::exists(newFilePath)); std::ifstream file(newFilePath); - std::string fileContent((std::istreambuf_iterator(file)), + std::string fileContent((std::istreambuf_iterator(file)), std::istreambuf_iterator()); EXPECT_EQ(fileContent, content); } @@ -170,24 +170,24 @@ TEST_F(AsyncIOTest, AsyncFileWriteNewFile) { TEST_F(AsyncIOTest, AsyncFileWriteExistingFile) { std::promise> promise; auto future = promise.get_future(); - + std::string content = "Updated content"; fs::path filePath = testDir / "file1.txt"; - - async_file->asyncWrite(filePath, std::span(content.data(), content.size()), + + async_file->asyncWrite(filePath, std::span(content.data(), content.size()), [&promise](AsyncResult result) { promise.set_value(std::move(result)); }); - + ASSERT_TRUE(waitForFuture(future)); auto result = future.get(); - + EXPECT_TRUE(result.success); EXPECT_TRUE(result.error_message.empty()); - + // Verify file was updated with correct content std::ifstream file(filePath); - std::string fileContent((std::istreambuf_iterator(file)), + std::string fileContent((std::istreambuf_iterator(file)), std::istreambuf_iterator()); EXPECT_EQ(fileContent, content); } @@ -196,21 +196,21 @@ TEST_F(AsyncIOTest, AsyncFileWriteExistingFile) { TEST_F(AsyncIOTest, AsyncFileDeleteExistingFile) { std::promise> promise; auto future = promise.get_future(); - + fs::path filePath = testDir / "file2.txt"; ASSERT_TRUE(fs::exists(filePath)); // Ensure file exists before test - - async_file->asyncDelete(filePath, + + async_file->asyncDelete(filePath, [&promise](AsyncResult result) { promise.set_value(std::move(result)); }); - + ASSERT_TRUE(waitForFuture(future)); auto result = future.get(); - + EXPECT_TRUE(result.success); EXPECT_TRUE(result.error_message.empty()); - + // Verify file was deleted EXPECT_FALSE(fs::exists(filePath)); } @@ -219,17 +219,17 @@ TEST_F(AsyncIOTest, AsyncFileDeleteExistingFile) { TEST_F(AsyncIOTest, AsyncFileDeleteNonExistentFile) { std::promise> promise; auto future = promise.get_future(); - + fs::path filePath = testDir / "non_existent.txt"; - - async_file->asyncDelete(filePath, + + async_file->asyncDelete(filePath, [&promise](AsyncResult result) { promise.set_value(std::move(result)); }); - + ASSERT_TRUE(waitForFuture(future)); auto result = future.get(); - + EXPECT_FALSE(result.success); EXPECT_THAT(result.error_message, HasSubstr("does not exist")); } @@ -238,33 +238,33 @@ TEST_F(AsyncIOTest, AsyncFileDeleteNonExistentFile) { TEST_F(AsyncIOTest, AsyncFileCopyExistingFile) { std::promise> promise; auto future = promise.get_future(); - + fs::path srcPath = testDir / "file1.txt"; fs::path destPath = testDir / "file1_copy.txt"; - - async_file->asyncCopy(srcPath, destPath, + + async_file->asyncCopy(srcPath, destPath, [&promise](AsyncResult result) { promise.set_value(std::move(result)); }); - + ASSERT_TRUE(waitForFuture(future)); auto result = future.get(); - + EXPECT_TRUE(result.success); EXPECT_TRUE(result.error_message.empty()); - + // Verify file was copied EXPECT_TRUE(fs::exists(destPath)); - + // Verify content is the same std::ifstream srcFile(srcPath); - std::string srcContent((std::istreambuf_iterator(srcFile)), + std::string srcContent((std::istreambuf_iterator(srcFile)), std::istreambuf_iterator()); - + std::ifstream destFile(destPath); - std::string destContent((std::istreambuf_iterator(destFile)), + std::string destContent((std::istreambuf_iterator(destFile)), std::istreambuf_iterator()); - + EXPECT_EQ(srcContent, destContent); } @@ -272,21 +272,21 @@ TEST_F(AsyncIOTest, AsyncFileCopyExistingFile) { TEST_F(AsyncIOTest, AsyncFileCopyNonExistentSource) { std::promise> promise; auto future = promise.get_future(); - + fs::path srcPath = testDir / "non_existent.txt"; fs::path destPath = testDir / "copy_fail.txt"; - - async_file->asyncCopy(srcPath, destPath, + + async_file->asyncCopy(srcPath, destPath, [&promise](AsyncResult result) { promise.set_value(std::move(result)); }); - + ASSERT_TRUE(waitForFuture(future)); auto result = future.get(); - + EXPECT_FALSE(result.success); EXPECT_THAT(result.error_message, HasSubstr("does not exist")); - + // Verify destination was not created EXPECT_FALSE(fs::exists(destPath)); } @@ -295,35 +295,35 @@ TEST_F(AsyncIOTest, AsyncFileCopyNonExistentSource) { TEST_F(AsyncIOTest, AsyncFileReadWithTimeoutSuccess) { std::promise> promise; auto future = promise.get_future(); - - async_file->asyncReadWithTimeout(testDir / "file1.txt", std::chrono::milliseconds(500), + + async_file->asyncReadWithTimeout(testDir / "file1.txt", std::chrono::milliseconds(500), [&promise](AsyncResult result) { promise.set_value(std::move(result)); }); - + ASSERT_TRUE(waitForFuture(future, 1000)); auto result = future.get(); - + EXPECT_TRUE(result.success); EXPECT_EQ(result.value, "Test file 1 content"); EXPECT_TRUE(result.error_message.empty()); } -// Test AsyncFile::asyncReadWithTimeout that times out +// Test AsyncFile::asyncReadWithTimeout that times out // (this test may be flaky depending on implementation details) TEST_F(AsyncIOTest, AsyncFileReadWithTimeoutExpires) { std::promise> promise; auto future = promise.get_future(); - + // Assuming implementation adds artificial delay, set very short timeout - async_file->asyncReadWithTimeout(testDir / "file1.txt", std::chrono::milliseconds(1), + async_file->asyncReadWithTimeout(testDir / "file1.txt", std::chrono::milliseconds(1), [&promise](AsyncResult result) { promise.set_value(std::move(result)); }); - + ASSERT_TRUE(waitForFuture(future, 200)); auto result = future.get(); - + // If the operation timed out, result.success should be false if (!result.success) { EXPECT_THAT(result.error_message, HasSubstr("timeout")); @@ -337,20 +337,20 @@ TEST_F(AsyncIOTest, AsyncFileReadWithTimeoutExpires) { TEST_F(AsyncIOTest, AsyncFileBatchReadExistingFiles) { std::promise>> promise; auto future = promise.get_future(); - + std::vector filePaths = { (testDir / "file1.txt").string(), (testDir / "file2.txt").string() }; - - async_file->asyncBatchRead(filePaths, + + async_file->asyncBatchRead(filePaths, [&promise](AsyncResult> result) { promise.set_value(std::move(result)); }); - + ASSERT_TRUE(waitForFuture(future)); auto result = future.get(); - + EXPECT_TRUE(result.success); EXPECT_TRUE(result.error_message.empty()); ASSERT_EQ(result.value.size(), 2); @@ -362,20 +362,20 @@ TEST_F(AsyncIOTest, AsyncFileBatchReadExistingFiles) { TEST_F(AsyncIOTest, AsyncFileBatchReadMixedFiles) { std::promise>> promise; auto future = promise.get_future(); - + std::vector filePaths = { (testDir / "file1.txt").string(), (testDir / "non_existent.txt").string() }; - - async_file->asyncBatchRead(filePaths, + + async_file->asyncBatchRead(filePaths, [&promise](AsyncResult> result) { promise.set_value(std::move(result)); }); - + ASSERT_TRUE(waitForFuture(future)); auto result = future.get(); - + EXPECT_FALSE(result.success); EXPECT_THAT(result.error_message, HasSubstr("non_existent.txt")); } @@ -384,15 +384,15 @@ TEST_F(AsyncIOTest, AsyncFileBatchReadMixedFiles) { TEST_F(AsyncIOTest, AsyncFileStatExistingFile) { std::promise> promise; auto future = promise.get_future(); - - async_file->asyncStat(testDir / "file1.txt", + + async_file->asyncStat(testDir / "file1.txt", [&promise](AsyncResult result) { promise.set_value(std::move(result)); }); - + ASSERT_TRUE(waitForFuture(future)); auto result = future.get(); - + EXPECT_TRUE(result.success); EXPECT_TRUE(result.error_message.empty()); EXPECT_EQ(fs::is_regular_file(result.value), true); @@ -402,15 +402,15 @@ TEST_F(AsyncIOTest, AsyncFileStatExistingFile) { TEST_F(AsyncIOTest, AsyncFileStatNonExistentFile) { std::promise> promise; auto future = promise.get_future(); - - async_file->asyncStat(testDir / "non_existent.txt", + + async_file->asyncStat(testDir / "non_existent.txt", [&promise](AsyncResult result) { promise.set_value(std::move(result)); }); - + ASSERT_TRUE(waitForFuture(future)); auto result = future.get(); - + EXPECT_FALSE(result.success); EXPECT_THAT(result.error_message, HasSubstr("does not exist")); } @@ -419,21 +419,21 @@ TEST_F(AsyncIOTest, AsyncFileStatNonExistentFile) { TEST_F(AsyncIOTest, AsyncFileMoveExistingFile) { std::promise> promise; auto future = promise.get_future(); - + fs::path srcPath = testDir / "file1.txt"; fs::path destPath = testDir / "file1_moved.txt"; - - async_file->asyncMove(srcPath, destPath, + + async_file->asyncMove(srcPath, destPath, [&promise](AsyncResult result) { promise.set_value(std::move(result)); }); - + ASSERT_TRUE(waitForFuture(future)); auto result = future.get(); - + EXPECT_TRUE(result.success); EXPECT_TRUE(result.error_message.empty()); - + // Verify file was moved EXPECT_FALSE(fs::exists(srcPath)); EXPECT_TRUE(fs::exists(destPath)); @@ -443,21 +443,21 @@ TEST_F(AsyncIOTest, AsyncFileMoveExistingFile) { TEST_F(AsyncIOTest, AsyncFileMoveNonExistentSource) { std::promise> promise; auto future = promise.get_future(); - + fs::path srcPath = testDir / "non_existent.txt"; fs::path destPath = testDir / "move_fail.txt"; - - async_file->asyncMove(srcPath, destPath, + + async_file->asyncMove(srcPath, destPath, [&promise](AsyncResult result) { promise.set_value(std::move(result)); }); - + ASSERT_TRUE(waitForFuture(future)); auto result = future.get(); - + EXPECT_FALSE(result.success); EXPECT_THAT(result.error_message, HasSubstr("does not exist")); - + // Verify destination was not created EXPECT_FALSE(fs::exists(destPath)); } @@ -466,20 +466,20 @@ TEST_F(AsyncIOTest, AsyncFileMoveNonExistentSource) { TEST_F(AsyncIOTest, AsyncFileChangePermissionsExistingFile) { std::promise> promise; auto future = promise.get_future(); - + fs::path filePath = testDir / "file1.txt"; - - async_file->asyncChangePermissions(filePath, fs::perms::owner_read | fs::perms::owner_write, + + async_file->asyncChangePermissions(filePath, fs::perms::owner_read | fs::perms::owner_write, [&promise](AsyncResult result) { promise.set_value(std::move(result)); }); - + ASSERT_TRUE(waitForFuture(future)); auto result = future.get(); - + EXPECT_TRUE(result.success); EXPECT_TRUE(result.error_message.empty()); - + // Verify permissions were changed (implementation-dependent) // This might be system-dependent, so we're not checking the actual permissions } @@ -488,20 +488,20 @@ TEST_F(AsyncIOTest, AsyncFileChangePermissionsExistingFile) { TEST_F(AsyncIOTest, AsyncFileCreateDirectoryNew) { std::promise> promise; auto future = promise.get_future(); - + fs::path dirPath = testDir / "new_dir"; - - async_file->asyncCreateDirectory(dirPath, + + async_file->asyncCreateDirectory(dirPath, [&promise](AsyncResult result) { promise.set_value(std::move(result)); }); - + ASSERT_TRUE(waitForFuture(future)); auto result = future.get(); - + EXPECT_TRUE(result.success); EXPECT_TRUE(result.error_message.empty()); - + // Verify directory was created EXPECT_TRUE(fs::exists(dirPath)); EXPECT_TRUE(fs::is_directory(dirPath)); @@ -511,17 +511,17 @@ TEST_F(AsyncIOTest, AsyncFileCreateDirectoryNew) { TEST_F(AsyncIOTest, AsyncFileCreateDirectoryExisting) { std::promise> promise; auto future = promise.get_future(); - + fs::path dirPath = testDir / "subdir1"; - - async_file->asyncCreateDirectory(dirPath, + + async_file->asyncCreateDirectory(dirPath, [&promise](AsyncResult result) { promise.set_value(std::move(result)); }); - + ASSERT_TRUE(waitForFuture(future)); auto result = future.get(); - + EXPECT_FALSE(result.success); EXPECT_THAT(result.error_message, HasSubstr("already exists")); } @@ -530,15 +530,15 @@ TEST_F(AsyncIOTest, AsyncFileCreateDirectoryExisting) { TEST_F(AsyncIOTest, AsyncFileExistsExistingFile) { std::promise> promise; auto future = promise.get_future(); - - async_file->asyncExists(testDir / "file1.txt", + + async_file->asyncExists(testDir / "file1.txt", [&promise](AsyncResult result) { promise.set_value(std::move(result)); }); - + ASSERT_TRUE(waitForFuture(future)); auto result = future.get(); - + EXPECT_TRUE(result.success); EXPECT_TRUE(result.error_message.empty()); EXPECT_TRUE(result.value); @@ -548,15 +548,15 @@ TEST_F(AsyncIOTest, AsyncFileExistsExistingFile) { TEST_F(AsyncIOTest, AsyncFileExistsNonExistentFile) { std::promise> promise; auto future = promise.get_future(); - - async_file->asyncExists(testDir / "non_existent.txt", + + async_file->asyncExists(testDir / "non_existent.txt", [&promise](AsyncResult result) { promise.set_value(std::move(result)); }); - + ASSERT_TRUE(waitForFuture(future)); auto result = future.get(); - + EXPECT_TRUE(result.success); EXPECT_TRUE(result.error_message.empty()); EXPECT_FALSE(result.value); @@ -566,7 +566,7 @@ TEST_F(AsyncIOTest, AsyncFileExistsNonExistentFile) { TEST_F(AsyncIOTest, AsyncFileReadFileCoroutine) { auto fileTask = async_file->readFile(testDir / "file1.txt"); auto result = fileTask.get(); - + EXPECT_TRUE(result.success); EXPECT_EQ(result.value, "Test file 1 content"); EXPECT_TRUE(result.error_message.empty()); @@ -576,18 +576,18 @@ TEST_F(AsyncIOTest, AsyncFileReadFileCoroutine) { TEST_F(AsyncIOTest, AsyncFileWriteFileCoroutine) { std::string content = "Coroutine written content"; fs::path filePath = testDir / "coroutine_written.txt"; - - auto writeTask = async_file->writeFile(filePath, + + auto writeTask = async_file->writeFile(filePath, std::span(content.data(), content.size())); auto result = writeTask.get(); - + EXPECT_TRUE(result.success); EXPECT_TRUE(result.error_message.empty()); - + // Verify file was created with correct content EXPECT_TRUE(fs::exists(filePath)); std::ifstream file(filePath); - std::string fileContent((std::istreambuf_iterator(file)), + std::string fileContent((std::istreambuf_iterator(file)), std::istreambuf_iterator()); EXPECT_EQ(fileContent, content); } @@ -596,20 +596,20 @@ TEST_F(AsyncIOTest, AsyncFileWriteFileCoroutine) { TEST_F(AsyncIOTest, AsyncDirectoryCreateNew) { std::promise> promise; auto future = promise.get_future(); - + fs::path dirPath = testDir / "async_dir_new"; - - async_dir->asyncCreate(dirPath, + + async_dir->asyncCreate(dirPath, [&promise](AsyncResult result) { promise.set_value(std::move(result)); }); - + ASSERT_TRUE(waitForFuture(future)); auto result = future.get(); - + EXPECT_TRUE(result.success); EXPECT_TRUE(result.error_message.empty()); - + // Verify directory was created EXPECT_TRUE(fs::exists(dirPath)); EXPECT_TRUE(fs::is_directory(dirPath)); @@ -619,20 +619,20 @@ TEST_F(AsyncIOTest, AsyncDirectoryCreateNew) { TEST_F(AsyncIOTest, AsyncDirectoryRemoveExisting) { std::promise> promise; auto future = promise.get_future(); - + fs::path dirPath = testDir / "subdir2"; - - async_dir->asyncRemove(dirPath, + + async_dir->asyncRemove(dirPath, [&promise](AsyncResult result) { promise.set_value(std::move(result)); }); - + ASSERT_TRUE(waitForFuture(future)); auto result = future.get(); - + EXPECT_TRUE(result.success); EXPECT_TRUE(result.error_message.empty()); - + // Verify directory was removed EXPECT_FALSE(fs::exists(dirPath)); } @@ -641,30 +641,30 @@ TEST_F(AsyncIOTest, AsyncDirectoryRemoveExisting) { TEST_F(AsyncIOTest, AsyncDirectoryListContentsExisting) { std::promise>> promise; auto future = promise.get_future(); - - async_dir->asyncListContents(testDir, + + async_dir->asyncListContents(testDir, [&promise](AsyncResult> result) { promise.set_value(std::move(result)); }); - + ASSERT_TRUE(waitForFuture(future)); auto result = future.get(); - + EXPECT_TRUE(result.success); EXPECT_TRUE(result.error_message.empty()); - + // Verify we have the expected number of entries EXPECT_GE(result.value.size(), 5); // At least 5 entries (files and dirs) - + // Check for known files and directories bool foundFile1 = false; bool foundSubdir1 = false; - + for (const auto& entry : result.value) { if (entry.filename() == "file1.txt") foundFile1 = true; if (entry.filename() == "subdir1") foundSubdir1 = true; } - + EXPECT_TRUE(foundFile1); EXPECT_TRUE(foundSubdir1); } @@ -673,15 +673,15 @@ TEST_F(AsyncIOTest, AsyncDirectoryListContentsExisting) { TEST_F(AsyncIOTest, AsyncDirectoryListContentsNonExistent) { std::promise>> promise; auto future = promise.get_future(); - - async_dir->asyncListContents(testDir / "non_existent_dir", + + async_dir->asyncListContents(testDir / "non_existent_dir", [&promise](AsyncResult> result) { promise.set_value(std::move(result)); }); - + ASSERT_TRUE(waitForFuture(future)); auto result = future.get(); - + EXPECT_FALSE(result.success); EXPECT_THAT(result.error_message, HasSubstr("does not exist")); } @@ -690,15 +690,15 @@ TEST_F(AsyncIOTest, AsyncDirectoryListContentsNonExistent) { TEST_F(AsyncIOTest, AsyncDirectoryExistsExisting) { std::promise> promise; auto future = promise.get_future(); - - async_dir->asyncExists(testDir / "subdir1", + + async_dir->asyncExists(testDir / "subdir1", [&promise](AsyncResult result) { promise.set_value(std::move(result)); }); - + ASSERT_TRUE(waitForFuture(future)); auto result = future.get(); - + EXPECT_TRUE(result.success); EXPECT_TRUE(result.error_message.empty()); EXPECT_TRUE(result.value); @@ -708,15 +708,15 @@ TEST_F(AsyncIOTest, AsyncDirectoryExistsExisting) { TEST_F(AsyncIOTest, AsyncDirectoryExistsNonExistent) { std::promise> promise; auto future = promise.get_future(); - - async_dir->asyncExists(testDir / "non_existent_dir", + + async_dir->asyncExists(testDir / "non_existent_dir", [&promise](AsyncResult result) { promise.set_value(std::move(result)); }); - + ASSERT_TRUE(waitForFuture(future)); auto result = future.get(); - + EXPECT_TRUE(result.success); EXPECT_TRUE(result.error_message.empty()); EXPECT_FALSE(result.value); @@ -726,22 +726,22 @@ TEST_F(AsyncIOTest, AsyncDirectoryExistsNonExistent) { TEST_F(AsyncIOTest, AsyncDirectoryListContentsCoroutine) { auto listTask = async_dir->listContents(testDir); auto result = listTask.get(); - + EXPECT_TRUE(result.success); EXPECT_TRUE(result.error_message.empty()); - + // Verify we have the expected number of entries EXPECT_GE(result.value.size(), 5); // At least 5 entries (files and dirs) - + // Check for known files and directories bool foundFile1 = false; bool foundSubdir1 = false; - + for (const auto& entry : result.value) { if (entry.filename() == "file1.txt") foundFile1 = true; if (entry.filename() == "subdir1") foundSubdir1 = true; } - + EXPECT_TRUE(foundFile1); EXPECT_TRUE(foundSubdir1); } @@ -750,16 +750,16 @@ TEST_F(AsyncIOTest, AsyncDirectoryListContentsCoroutine) { TEST_F(AsyncIOTest, InvalidInputHandling) { std::promise> readPromise; auto readFuture = readPromise.get_future(); - + // Empty filename - async_file->asyncRead("", + async_file->asyncRead("", [&readPromise](AsyncResult result) { readPromise.set_value(std::move(result)); }); - + ASSERT_TRUE(waitForFuture(readFuture)); auto readResult = readFuture.get(); - + EXPECT_FALSE(readResult.success); EXPECT_THAT(readResult.error_message, HasSubstr("Invalid")); } @@ -769,24 +769,24 @@ TEST_F(AsyncIOTest, ConcurrentOperations) { constexpr int numConcurrentOps = 10; std::vector>> promises(numConcurrentOps); std::vector>> futures; - + for (int i = 0; i < numConcurrentOps; i++) { futures.push_back(promises[i].get_future()); } - + // Start multiple reads concurrently for (int i = 0; i < numConcurrentOps; i++) { - async_file->asyncRead(testDir / "file1.txt", + async_file->asyncRead(testDir / "file1.txt", [&promises, i](AsyncResult result) { promises[i].set_value(std::move(result)); }); } - + // Wait for all operations to complete for (int i = 0; i < numConcurrentOps; i++) { ASSERT_TRUE(waitForFuture(futures[i])); auto result = futures[i].get(); - + EXPECT_TRUE(result.success); EXPECT_EQ(result.value, "Test file 1 content"); } @@ -797,19 +797,19 @@ TEST_F(AsyncIOTest, TaskFunctionality) { // Create a task manually std::promise> promise; auto future = promise.get_future(); - + Task> task(std::move(future)); - + // Set a value to the promise AsyncResult expectedResult; expectedResult.success = true; expectedResult.value = "Task test value"; - + promise.set_value(expectedResult); - + // Check if task is ready EXPECT_TRUE(task.is_ready()); - + // Get the result auto result = task.get(); EXPECT_TRUE(result.success); @@ -819,4 +819,4 @@ TEST_F(AsyncIOTest, TaskFunctionality) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/io/test_compress.cpp b/tests/io/test_compress.cpp index e3ecb246..fbf11f29 100644 --- a/tests/io/test_compress.cpp +++ b/tests/io/test_compress.cpp @@ -924,4 +924,4 @@ TEST_F(FolderCompressionTest, DISABLED_CompressionPerformance) { double size_ratio = static_cast(par_size) / seq_size; EXPECT_NEAR(size_ratio, 1.0, 0.05); // Allow 5% difference -} \ No newline at end of file +} diff --git a/tests/io/test_file_permission.cpp b/tests/io/test_file_permission.cpp index a88f4fc7..c8ef75cf 100644 --- a/tests/io/test_file_permission.cpp +++ b/tests/io/test_file_permission.cpp @@ -319,4 +319,4 @@ TEST_F(FilePermissionTest, ThreadSafety) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/io/test_glob.cpp b/tests/io/test_glob.cpp index 390082ba..28e282d1 100644 --- a/tests/io/test_glob.cpp +++ b/tests/io/test_glob.cpp @@ -328,4 +328,4 @@ TEST_F(GlobTest, DirectoryIteration) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/io/test_pushd.cpp b/tests/io/test_pushd.cpp index 9c8aa0b8..d8b91876 100644 --- a/tests/io/test_pushd.cpp +++ b/tests/io/test_pushd.cpp @@ -571,4 +571,4 @@ TEST_F(DirectoryStackTest, MoveOperations) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/memory/test_memory.cpp b/tests/memory/test_memory.cpp index abc12240..6c1b1ca9 100644 --- a/tests/memory/test_memory.cpp +++ b/tests/memory/test_memory.cpp @@ -441,4 +441,4 @@ TEST_F(MemoryPoolTest, MemoryLeakCheck) { // No way to directly verify cleanup after destruction, but we can // at least verify the test ran without memory errors -} \ No newline at end of file +} diff --git a/tests/memory/test_object.cpp b/tests/memory/test_object.cpp index c89286c6..0e23a381 100644 --- a/tests/memory/test_object.cpp +++ b/tests/memory/test_object.cpp @@ -596,4 +596,4 @@ TEST_F(ObjectPoolTest, PerformanceComparison) { // Pool should generally be faster after warmup, but we don't assert this // as performance can vary by platform -} \ No newline at end of file +} diff --git a/tests/memory/test_ring.cpp b/tests/memory/test_ring.cpp index f5ca6aea..e6ab75d4 100644 --- a/tests/memory/test_ring.cpp +++ b/tests/memory/test_ring.cpp @@ -687,4 +687,4 @@ TEST_F(RingBufferTest, EmptyIterator) { EXPECT_EQ(*it, 42); } EXPECT_EQ(count, 1); -} \ No newline at end of file +} diff --git a/tests/memory/test_shared.cpp b/tests/memory/test_shared.cpp index bd5cb1a8..5c4251d8 100644 --- a/tests/memory/test_shared.cpp +++ b/tests/memory/test_shared.cpp @@ -736,4 +736,4 @@ TEST_F(SharedMemoryTest, InitializationFailures) { << "'"; } } -} \ No newline at end of file +} diff --git a/tests/memory/test_short_alloc.cpp b/tests/memory/test_short_alloc.cpp index 1c347bac..2001c6bc 100644 --- a/tests/memory/test_short_alloc.cpp +++ b/tests/memory/test_short_alloc.cpp @@ -624,4 +624,4 @@ TEST(UtilsTest, MemoryFill) { for (int i = 0; i < 1024; i++) { EXPECT_EQ(static_cast(buffer[i]), utils::getFreedPattern()); } -} \ No newline at end of file +} diff --git a/tests/memory/test_utils.cpp b/tests/memory/test_utils.cpp index 767008a8..4029c3f1 100644 --- a/tests/memory/test_utils.cpp +++ b/tests/memory/test_utils.cpp @@ -296,4 +296,4 @@ TEST(MemoryUtilsTest, Config) { #else EXPECT_FALSE(Config::EnableMemoryTracking); #endif -} \ No newline at end of file +} diff --git a/tests/meta/test_bind_first.hpp b/tests/meta/test_bind_first.hpp index 169374a7..a3f03216 100644 --- a/tests/meta/test_bind_first.hpp +++ b/tests/meta/test_bind_first.hpp @@ -350,4 +350,4 @@ TEST_F(BindFirstTest, AwaitableCreation) { } // namespace atom::test -#endif // ATOM_TEST_BIND_FIRST_HPP \ No newline at end of file +#endif // ATOM_TEST_BIND_FIRST_HPP diff --git a/tests/meta/test_container_traits.hpp b/tests/meta/test_container_traits.hpp index 21f59c52..3dd4ac1d 100644 --- a/tests/meta/test_container_traits.hpp +++ b/tests/meta/test_container_traits.hpp @@ -39,20 +39,20 @@ class ContainerTraitsTest : public ::testing::Test { // Test std::vector traits TEST_F(ContainerTraitsTest, VectorTraits) { using VectorTraits = atom::meta::ContainerTraits>; - + // Container category EXPECT_TRUE(VectorTraits::is_sequence_container); EXPECT_FALSE(VectorTraits::is_associative_container); EXPECT_FALSE(VectorTraits::is_unordered_associative_container); EXPECT_FALSE(VectorTraits::is_container_adapter); - + // Iterator capabilities EXPECT_TRUE(VectorTraits::has_random_access); EXPECT_TRUE(VectorTraits::has_bidirectional_access); EXPECT_FALSE(VectorTraits::has_forward_access); EXPECT_TRUE(VectorTraits::has_begin_end); EXPECT_TRUE(VectorTraits::has_rbegin_rend); - + // Container operations EXPECT_TRUE(VectorTraits::has_size); EXPECT_TRUE(VectorTraits::has_empty); @@ -68,25 +68,25 @@ TEST_F(ContainerTraitsTest, VectorTraits) { EXPECT_TRUE(VectorTraits::has_emplace); EXPECT_FALSE(VectorTraits::has_emplace_front); EXPECT_TRUE(VectorTraits::has_emplace_back); - + // Memory management EXPECT_TRUE(VectorTraits::has_reserve); EXPECT_TRUE(VectorTraits::has_capacity); EXPECT_TRUE(VectorTraits::has_shrink_to_fit); - + // Access operations EXPECT_TRUE(VectorTraits::has_subscript); EXPECT_TRUE(VectorTraits::has_at); EXPECT_FALSE(VectorTraits::has_find); EXPECT_FALSE(VectorTraits::has_count); - + // Container properties EXPECT_FALSE(VectorTraits::has_key_type); EXPECT_FALSE(VectorTraits::has_mapped_type); EXPECT_FALSE(VectorTraits::is_sorted); EXPECT_FALSE(VectorTraits::is_unique); EXPECT_FALSE(VectorTraits::is_fixed_size); - + // Type checks static_assert(std::is_same_v); static_assert(std::is_same_v>); @@ -95,15 +95,15 @@ TEST_F(ContainerTraitsTest, VectorTraits) { // Test std::deque traits TEST_F(ContainerTraitsTest, DequeTraits) { using DequeTraits = atom::meta::ContainerTraits>; - + // Container category EXPECT_TRUE(DequeTraits::is_sequence_container); EXPECT_FALSE(DequeTraits::is_associative_container); - + // Iterator capabilities EXPECT_TRUE(DequeTraits::has_random_access); EXPECT_TRUE(DequeTraits::has_bidirectional_access); - + // Container operations - deque supports both front and back operations EXPECT_TRUE(DequeTraits::has_front); EXPECT_TRUE(DequeTraits::has_back); @@ -113,16 +113,16 @@ TEST_F(ContainerTraitsTest, DequeTraits) { EXPECT_TRUE(DequeTraits::has_pop_back); EXPECT_TRUE(DequeTraits::has_emplace_front); EXPECT_TRUE(DequeTraits::has_emplace_back); - + // Access operations EXPECT_TRUE(DequeTraits::has_subscript); EXPECT_TRUE(DequeTraits::has_at); - + // Memory management - deque doesn't have reserve/capacity EXPECT_FALSE(DequeTraits::has_reserve); EXPECT_FALSE(DequeTraits::has_capacity); EXPECT_TRUE(DequeTraits::has_shrink_to_fit); - + // Container properties EXPECT_FALSE(DequeTraits::is_fixed_size); } @@ -130,15 +130,15 @@ TEST_F(ContainerTraitsTest, DequeTraits) { // Test std::list traits TEST_F(ContainerTraitsTest, ListTraits) { using ListTraits = atom::meta::ContainerTraits>; - + // Container category EXPECT_TRUE(ListTraits::is_sequence_container); - + // Iterator capabilities - list has bidirectional but not random access EXPECT_FALSE(ListTraits::has_random_access); EXPECT_TRUE(ListTraits::has_bidirectional_access); EXPECT_FALSE(ListTraits::has_forward_access); - + // Container operations EXPECT_TRUE(ListTraits::has_front); EXPECT_TRUE(ListTraits::has_back); @@ -148,11 +148,11 @@ TEST_F(ContainerTraitsTest, ListTraits) { EXPECT_TRUE(ListTraits::has_pop_back); EXPECT_TRUE(ListTraits::has_emplace_front); EXPECT_TRUE(ListTraits::has_emplace_back); - + // Access operations - list doesn't support random access EXPECT_FALSE(ListTraits::has_subscript); EXPECT_FALSE(ListTraits::has_at); - + // Memory management - list doesn't have reserve/capacity EXPECT_FALSE(ListTraits::has_reserve); EXPECT_FALSE(ListTraits::has_capacity); @@ -162,16 +162,16 @@ TEST_F(ContainerTraitsTest, ListTraits) { // Test std::forward_list traits TEST_F(ContainerTraitsTest, ForwardListTraits) { using ForwardListTraits = atom::meta::ContainerTraits>; - + // Container category EXPECT_TRUE(ForwardListTraits::is_sequence_container); - + // Iterator capabilities - forward_list only has forward iterators EXPECT_FALSE(ForwardListTraits::has_random_access); EXPECT_FALSE(ForwardListTraits::has_bidirectional_access); EXPECT_TRUE(ForwardListTraits::has_forward_access); EXPECT_FALSE(ForwardListTraits::has_rbegin_rend); - + // Container operations - forward_list only supports front operations EXPECT_TRUE(ForwardListTraits::has_front); EXPECT_FALSE(ForwardListTraits::has_back); @@ -181,10 +181,10 @@ TEST_F(ContainerTraitsTest, ForwardListTraits) { EXPECT_FALSE(ForwardListTraits::has_pop_back); EXPECT_TRUE(ForwardListTraits::has_emplace_front); EXPECT_FALSE(ForwardListTraits::has_emplace_back); - + // Special property - forward_list doesn't have size() EXPECT_FALSE(ForwardListTraits::has_size); - + // Access operations EXPECT_FALSE(ForwardListTraits::has_subscript); EXPECT_FALSE(ForwardListTraits::has_at); @@ -193,14 +193,14 @@ TEST_F(ContainerTraitsTest, ForwardListTraits) { // Test std::array traits TEST_F(ContainerTraitsTest, ArrayTraits) { using ArrayTraits = atom::meta::ContainerTraits>; - + // Container category EXPECT_TRUE(ArrayTraits::is_sequence_container); - + // Iterator capabilities EXPECT_TRUE(ArrayTraits::has_random_access); EXPECT_TRUE(ArrayTraits::has_bidirectional_access); - + // Container operations EXPECT_TRUE(ArrayTraits::has_front); EXPECT_TRUE(ArrayTraits::has_back); @@ -210,16 +210,16 @@ TEST_F(ContainerTraitsTest, ArrayTraits) { EXPECT_FALSE(ArrayTraits::has_pop_back); EXPECT_FALSE(ArrayTraits::has_insert); EXPECT_FALSE(ArrayTraits::has_erase); - + // Access operations EXPECT_TRUE(ArrayTraits::has_subscript); EXPECT_TRUE(ArrayTraits::has_at); - + // Special properties - array is fixed size and cannot be cleared EXPECT_TRUE(ArrayTraits::is_fixed_size); EXPECT_FALSE(ArrayTraits::has_clear); EXPECT_EQ(ArrayTraits::array_size, 5); - + // Memory management - arrays don't have these operations EXPECT_FALSE(ArrayTraits::has_reserve); EXPECT_FALSE(ArrayTraits::has_capacity); @@ -229,14 +229,14 @@ TEST_F(ContainerTraitsTest, ArrayTraits) { // Test std::string traits TEST_F(ContainerTraitsTest, StringTraits) { using StringTraits = atom::meta::ContainerTraits; - + // Container category EXPECT_TRUE(StringTraits::is_sequence_container); - + // Iterator capabilities EXPECT_TRUE(StringTraits::has_random_access); EXPECT_TRUE(StringTraits::has_bidirectional_access); - + // Container operations EXPECT_TRUE(StringTraits::has_front); EXPECT_TRUE(StringTraits::has_back); @@ -244,17 +244,17 @@ TEST_F(ContainerTraitsTest, StringTraits) { EXPECT_TRUE(StringTraits::has_push_back); EXPECT_FALSE(StringTraits::has_pop_front); EXPECT_TRUE(StringTraits::has_pop_back); - + // Access operations EXPECT_TRUE(StringTraits::has_subscript); EXPECT_TRUE(StringTraits::has_at); EXPECT_TRUE(StringTraits::has_find); // string has find method - + // Memory management EXPECT_TRUE(StringTraits::has_reserve); EXPECT_TRUE(StringTraits::has_capacity); EXPECT_TRUE(StringTraits::has_shrink_to_fit); - + // Container properties EXPECT_FALSE(StringTraits::is_fixed_size); } @@ -264,41 +264,41 @@ TEST_F(ContainerTraitsTest, StringTraits) { // Test std::map traits TEST_F(ContainerTraitsTest, MapTraits) { using MapTraits = atom::meta::ContainerTraits>; - + // Container category EXPECT_FALSE(MapTraits::is_sequence_container); EXPECT_TRUE(MapTraits::is_associative_container); EXPECT_FALSE(MapTraits::is_unordered_associative_container); EXPECT_FALSE(MapTraits::is_container_adapter); - + // Iterator capabilities EXPECT_FALSE(MapTraits::has_random_access); EXPECT_TRUE(MapTraits::has_bidirectional_access); EXPECT_FALSE(MapTraits::has_forward_access); - + // Container operations EXPECT_TRUE(MapTraits::has_insert); EXPECT_TRUE(MapTraits::has_erase); EXPECT_TRUE(MapTraits::has_emplace); EXPECT_TRUE(MapTraits::has_find); EXPECT_TRUE(MapTraits::has_count); - + // Access operations - map has operator[] EXPECT_TRUE(MapTraits::has_subscript); EXPECT_FALSE(MapTraits::has_at); // This might be incorrect, map does have at() - + // Key-value properties EXPECT_TRUE(MapTraits::has_key_type); EXPECT_TRUE(MapTraits::has_mapped_type); EXPECT_TRUE(MapTraits::is_sorted); EXPECT_TRUE(MapTraits::is_unique); - + // Front/back operations not supported EXPECT_FALSE(MapTraits::has_front); EXPECT_FALSE(MapTraits::has_back); EXPECT_FALSE(MapTraits::has_push_front); EXPECT_FALSE(MapTraits::has_push_back); - + // Type checks static_assert(std::is_same_v); static_assert(std::is_same_v); @@ -308,19 +308,19 @@ TEST_F(ContainerTraitsTest, MapTraits) { // Test std::multimap traits TEST_F(ContainerTraitsTest, MultimapTraits) { using MultimapTraits = atom::meta::ContainerTraits>; - + // Container category EXPECT_TRUE(MultimapTraits::is_associative_container); - + // Key-value properties - multimap allows duplicate keys EXPECT_TRUE(MultimapTraits::has_key_type); EXPECT_TRUE(MultimapTraits::has_mapped_type); EXPECT_TRUE(MultimapTraits::is_sorted); EXPECT_FALSE(MultimapTraits::is_unique); // multimap allows duplicates - + // Access operations - multimap doesn't have operator[] EXPECT_FALSE(MultimapTraits::has_subscript); - + // Other operations EXPECT_TRUE(MultimapTraits::has_find); EXPECT_TRUE(MultimapTraits::has_count); @@ -329,29 +329,29 @@ TEST_F(ContainerTraitsTest, MultimapTraits) { // Test std::set traits TEST_F(ContainerTraitsTest, SetTraits) { using SetTraits = atom::meta::ContainerTraits>; - + // Container category EXPECT_TRUE(SetTraits::is_associative_container); - + // Iterator capabilities EXPECT_TRUE(SetTraits::has_bidirectional_access); - + // Key properties EXPECT_TRUE(SetTraits::has_key_type); EXPECT_FALSE(SetTraits::has_mapped_type); // set doesn't have mapped_type EXPECT_TRUE(SetTraits::is_sorted); EXPECT_TRUE(SetTraits::is_unique); - + // Operations EXPECT_TRUE(SetTraits::has_insert); EXPECT_TRUE(SetTraits::has_erase); EXPECT_TRUE(SetTraits::has_find); EXPECT_TRUE(SetTraits::has_count); - + // Access operations - set doesn't have subscript or at EXPECT_FALSE(SetTraits::has_subscript); EXPECT_FALSE(SetTraits::has_at); - + // Type checks static_assert(std::is_same_v); static_assert(std::is_same_v); @@ -360,10 +360,10 @@ TEST_F(ContainerTraitsTest, SetTraits) { // Test std::multiset traits TEST_F(ContainerTraitsTest, MultisetTraits) { using MultisetTraits = atom::meta::ContainerTraits>; - + // Container category EXPECT_TRUE(MultisetTraits::is_associative_container); - + // Key properties - multiset allows duplicates EXPECT_TRUE(MultisetTraits::has_key_type); EXPECT_FALSE(MultisetTraits::has_mapped_type); @@ -376,18 +376,18 @@ TEST_F(ContainerTraitsTest, MultisetTraits) { // Test std::unordered_map traits TEST_F(ContainerTraitsTest, UnorderedMapTraits) { using UnorderedMapTraits = atom::meta::ContainerTraits>; - + // Container category EXPECT_FALSE(UnorderedMapTraits::is_sequence_container); EXPECT_FALSE(UnorderedMapTraits::is_associative_container); EXPECT_TRUE(UnorderedMapTraits::is_unordered_associative_container); EXPECT_FALSE(UnorderedMapTraits::is_container_adapter); - + // Iterator capabilities - unordered containers have forward iterators EXPECT_FALSE(UnorderedMapTraits::has_random_access); EXPECT_FALSE(UnorderedMapTraits::has_bidirectional_access); EXPECT_TRUE(UnorderedMapTraits::has_forward_access); - + // Container operations EXPECT_TRUE(UnorderedMapTraits::has_insert); EXPECT_TRUE(UnorderedMapTraits::has_erase); @@ -395,16 +395,16 @@ TEST_F(ContainerTraitsTest, UnorderedMapTraits) { EXPECT_TRUE(UnorderedMapTraits::has_find); EXPECT_TRUE(UnorderedMapTraits::has_count); EXPECT_TRUE(UnorderedMapTraits::has_reserve); - + // Access operations EXPECT_TRUE(UnorderedMapTraits::has_subscript); - + // Key-value properties EXPECT_TRUE(UnorderedMapTraits::has_key_type); EXPECT_TRUE(UnorderedMapTraits::has_mapped_type); EXPECT_FALSE(UnorderedMapTraits::is_sorted); // unordered containers are not sorted EXPECT_TRUE(UnorderedMapTraits::is_unique); - + // Type checks static_assert(std::is_same_v); static_assert(std::is_same_v); @@ -413,16 +413,16 @@ TEST_F(ContainerTraitsTest, UnorderedMapTraits) { // Test std::unordered_multimap traits TEST_F(ContainerTraitsTest, UnorderedMultimapTraits) { using UnorderedMultimapTraits = atom::meta::ContainerTraits>; - + // Container category EXPECT_TRUE(UnorderedMultimapTraits::is_unordered_associative_container); - + // Key-value properties EXPECT_TRUE(UnorderedMultimapTraits::has_key_type); EXPECT_TRUE(UnorderedMultimapTraits::has_mapped_type); EXPECT_FALSE(UnorderedMultimapTraits::is_sorted); EXPECT_FALSE(UnorderedMultimapTraits::is_unique); // multimap allows duplicates - + // Access operations - unordered_multimap doesn't have operator[] EXPECT_FALSE(UnorderedMultimapTraits::has_subscript); } @@ -430,19 +430,19 @@ TEST_F(ContainerTraitsTest, UnorderedMultimapTraits) { // Test std::unordered_set traits TEST_F(ContainerTraitsTest, UnorderedSetTraits) { using UnorderedSetTraits = atom::meta::ContainerTraits>; - + // Container category EXPECT_TRUE(UnorderedSetTraits::is_unordered_associative_container); - + // Iterator capabilities EXPECT_TRUE(UnorderedSetTraits::has_forward_access); - + // Key properties EXPECT_TRUE(UnorderedSetTraits::has_key_type); EXPECT_FALSE(UnorderedSetTraits::has_mapped_type); EXPECT_FALSE(UnorderedSetTraits::is_sorted); EXPECT_TRUE(UnorderedSetTraits::is_unique); - + // Operations EXPECT_TRUE(UnorderedSetTraits::has_reserve); EXPECT_TRUE(UnorderedSetTraits::has_find); @@ -452,10 +452,10 @@ TEST_F(ContainerTraitsTest, UnorderedSetTraits) { // Test std::unordered_multiset traits TEST_F(ContainerTraitsTest, UnorderedMultisetTraits) { using UnorderedMultisetTraits = atom::meta::ContainerTraits>; - + // Container category EXPECT_TRUE(UnorderedMultisetTraits::is_unordered_associative_container); - + // Key properties EXPECT_TRUE(UnorderedMultisetTraits::has_key_type); EXPECT_FALSE(UnorderedMultisetTraits::has_mapped_type); @@ -468,17 +468,17 @@ TEST_F(ContainerTraitsTest, UnorderedMultisetTraits) { // Test std::stack traits TEST_F(ContainerTraitsTest, StackTraits) { using StackTraits = atom::meta::ContainerTraits>; - + // Container category EXPECT_FALSE(StackTraits::is_sequence_container); EXPECT_FALSE(StackTraits::is_associative_container); EXPECT_FALSE(StackTraits::is_unordered_associative_container); EXPECT_TRUE(StackTraits::is_container_adapter); - + // Iterator capabilities - adapters don't have iterators EXPECT_FALSE(StackTraits::has_begin_end); EXPECT_FALSE(StackTraits::has_rbegin_rend); - + // Container operations - stack only supports top, push, pop EXPECT_FALSE(StackTraits::has_front); EXPECT_TRUE(StackTraits::has_back); // top() is considered back @@ -486,16 +486,16 @@ TEST_F(ContainerTraitsTest, StackTraits) { EXPECT_TRUE(StackTraits::has_push_back); // push() is considered push_back EXPECT_FALSE(StackTraits::has_pop_front); EXPECT_TRUE(StackTraits::has_pop_back); // pop() is considered pop_back - + // Operations not supported by adapters EXPECT_FALSE(StackTraits::has_clear); EXPECT_FALSE(StackTraits::has_insert); EXPECT_FALSE(StackTraits::has_erase); - + // Access operations EXPECT_FALSE(StackTraits::has_subscript); EXPECT_FALSE(StackTraits::has_at); - + // Type checks static_assert(std::is_same_v); } @@ -503,10 +503,10 @@ TEST_F(ContainerTraitsTest, StackTraits) { // Test std::queue traits TEST_F(ContainerTraitsTest, QueueTraits) { using QueueTraits = atom::meta::ContainerTraits>; - + // Container category EXPECT_TRUE(QueueTraits::is_container_adapter); - + // Container operations - queue supports front, back, push, pop EXPECT_TRUE(QueueTraits::has_front); EXPECT_TRUE(QueueTraits::has_back); @@ -514,7 +514,7 @@ TEST_F(ContainerTraitsTest, QueueTraits) { EXPECT_TRUE(QueueTraits::has_push_back); // push() is considered push_back EXPECT_TRUE(QueueTraits::has_pop_front); // pop() is considered pop_front EXPECT_FALSE(QueueTraits::has_pop_back); - + // Iterator capabilities EXPECT_FALSE(QueueTraits::has_begin_end); } @@ -522,19 +522,19 @@ TEST_F(ContainerTraitsTest, QueueTraits) { // Test std::priority_queue traits TEST_F(ContainerTraitsTest, PriorityQueueTraits) { using PriorityQueueTraits = atom::meta::ContainerTraits>; - + // Container category EXPECT_TRUE(PriorityQueueTraits::is_container_adapter); - + // Container operations - priority_queue only supports top, push, pop EXPECT_FALSE(PriorityQueueTraits::has_front); EXPECT_TRUE(PriorityQueueTraits::has_back); // top() is considered back EXPECT_TRUE(PriorityQueueTraits::has_push_back); // push() EXPECT_TRUE(PriorityQueueTraits::has_pop_back); // pop() - + // Special property - priority_queue maintains heap order EXPECT_TRUE(PriorityQueueTraits::is_sorted); - + // Iterator capabilities EXPECT_FALSE(PriorityQueueTraits::has_begin_end); } @@ -545,7 +545,7 @@ TEST_F(ContainerTraitsTest, PriorityQueueTraits) { TEST_F(ContainerTraitsTest, ConstContainerTraits) { using ConstVectorTraits = atom::meta::ContainerTraits>; using VectorTraits = atom::meta::ContainerTraits>; - + // Const containers should have the same traits as non-const EXPECT_EQ(ConstVectorTraits::is_sequence_container, VectorTraits::is_sequence_container); EXPECT_EQ(ConstVectorTraits::has_random_access, VectorTraits::has_random_access); @@ -557,11 +557,11 @@ TEST_F(ContainerTraitsTest, ReferenceContainerTraits) { using VectorRefTraits = atom::meta::ContainerTraits&>; using VectorRValueRefTraits = atom::meta::ContainerTraits&&>; using VectorTraits = atom::meta::ContainerTraits>; - + // Reference containers should have the same traits as non-reference EXPECT_EQ(VectorRefTraits::is_sequence_container, VectorTraits::is_sequence_container); EXPECT_EQ(VectorRefTraits::has_random_access, VectorTraits::has_random_access); - + EXPECT_EQ(VectorRValueRefTraits::is_sequence_container, VectorTraits::is_sequence_container); EXPECT_EQ(VectorRValueRefTraits::has_random_access, VectorTraits::has_random_access); } @@ -573,55 +573,55 @@ TEST_F(ContainerTraitsTest, VariableTemplates) { // Sequence container checks EXPECT_TRUE(atom::meta::is_sequence_container_v>); EXPECT_FALSE(atom::meta::is_sequence_container_v>); - + // Associative container checks EXPECT_TRUE(atom::meta::is_associative_container_v>); EXPECT_FALSE(atom::meta::is_associative_container_v>); - + // Unordered associative container checks EXPECT_TRUE(atom::meta::is_unordered_associative_container_v>); EXPECT_FALSE(atom::meta::is_unordered_associative_container_v>); - + // Container adapter checks EXPECT_TRUE(atom::meta::is_container_adapter_v>); EXPECT_FALSE(atom::meta::is_container_adapter_v>); - + // Iterator capability checks EXPECT_TRUE(atom::meta::has_random_access_v>); EXPECT_FALSE(atom::meta::has_random_access_v>); - + EXPECT_TRUE(atom::meta::has_bidirectional_access_v>); EXPECT_FALSE(atom::meta::has_bidirectional_access_v>); - + EXPECT_TRUE(atom::meta::has_forward_access_v>); EXPECT_FALSE(atom::meta::has_forward_access_v>); - + // Operation capability checks EXPECT_TRUE(atom::meta::has_subscript_v>); EXPECT_FALSE(atom::meta::has_subscript_v>); - + EXPECT_TRUE(atom::meta::has_reserve_v>); EXPECT_FALSE(atom::meta::has_reserve_v>); - + EXPECT_TRUE(atom::meta::has_capacity_v>); EXPECT_FALSE(atom::meta::has_capacity_v>); - + EXPECT_TRUE(atom::meta::has_push_back_v>); EXPECT_FALSE(atom::meta::has_push_back_v>); - + EXPECT_TRUE(atom::meta::has_push_front_v>); EXPECT_FALSE(atom::meta::has_push_front_v>); - + EXPECT_TRUE(atom::meta::has_insert_v>); EXPECT_FALSE(atom::meta::has_insert_v>); - + // Container property checks EXPECT_TRUE(atom::meta::is_fixed_size_v>); EXPECT_FALSE(atom::meta::is_fixed_size_v>); - + EXPECT_TRUE(atom::meta::is_sorted_v>); EXPECT_FALSE(atom::meta::is_sorted_v>); - + EXPECT_TRUE(atom::meta::is_unique_v>); EXPECT_FALSE(atom::meta::is_unique_v>); } @@ -633,24 +633,24 @@ TEST_F(ContainerTraitsTest, GetIteratorCategory) { // Random access containers auto vectorCategory = atom::meta::get_iterator_category>(); static_assert(std::is_same_v); - + auto arrayCategory = atom::meta::get_iterator_category>(); static_assert(std::is_same_v); - + // Bidirectional containers auto listCategory = atom::meta::get_iterator_category>(); static_assert(std::is_same_v); - + auto mapCategory = atom::meta::get_iterator_category>(); static_assert(std::is_same_v); - + // Forward containers auto forwardListCategory = atom::meta::get_iterator_category>(); static_assert(std::is_same_v); - + auto unorderedMapCategory = atom::meta::get_iterator_category>(); static_assert(std::is_same_v); - + // Container adapters (input iterator as fallback) auto stackCategory = atom::meta::get_iterator_category>(); static_assert(std::is_same_v); @@ -663,14 +663,14 @@ TEST_F(ContainerTraitsTest, UtilityFunctions) { EXPECT_TRUE(atom::meta::supports_efficient_random_access>()); EXPECT_FALSE(atom::meta::supports_efficient_random_access>()); EXPECT_FALSE(atom::meta::supports_efficient_random_access>()); - + // Test can_grow_dynamically EXPECT_TRUE(atom::meta::can_grow_dynamically>()); EXPECT_TRUE(atom::meta::can_grow_dynamically>()); EXPECT_TRUE(atom::meta::can_grow_dynamically>()); EXPECT_FALSE(atom::meta::can_grow_dynamically>()); EXPECT_FALSE(atom::meta::can_grow_dynamically>()); // Adapters don't directly support growth - + // Test supports_key_lookup EXPECT_TRUE(atom::meta::supports_key_lookup>()); EXPECT_TRUE(atom::meta::supports_key_lookup>()); @@ -685,29 +685,29 @@ TEST_F(ContainerTraitsTest, UtilityFunctions) { TEST_F(ContainerTraitsTest, ContainerPipe) { // Create a test vector std::vector numbers = {1, 2, 3, 4, 5}; - + // Test transform operation auto pipe = atom::meta::make_container_pipe(numbers); auto doubled = pipe.transform([](int x) { return x * 2; }); auto result = doubled.get(); - + std::vector expected = {2, 4, 6, 8, 10}; EXPECT_EQ(result, expected); - + // Test filter operation auto filtered = atom::meta::make_container_pipe(numbers) .filter([](int x) { return x % 2 == 0; }); auto filteredResult = filtered.get(); - + std::vector expectedFiltered = {2, 4}; EXPECT_EQ(filteredResult, expectedFiltered); - + // Test chaining operations auto chained = atom::meta::make_container_pipe(numbers) .filter([](int x) { return x > 2; }) .transform([](int x) { return x * 3; }); auto chainedResult = chained.get(); - + std::vector expectedChained = {9, 12, 15}; // (3, 4, 5) * 3 EXPECT_EQ(chainedResult, expectedChained); } @@ -716,19 +716,19 @@ TEST_F(ContainerTraitsTest, ContainerPipe) { TEST_F(ContainerTraitsTest, ContainerPipeWithDifferentTypes) { // Test with list std::list words = {"hello", "world", "test"}; - + auto lengthPipe = atom::meta::make_container_pipe(words) .transform([](const std::string& s) { return s.length(); }); auto lengths = lengthPipe.get(); - + std::vector expectedLengths = {5, 5, 4}; EXPECT_EQ(lengths, expectedLengths); - + // Test filter with strings auto longWords = atom::meta::make_container_pipe(words) .filter([](const std::string& s) { return s.length() > 4; }); auto longWordsResult = longWords.get(); - + std::list expectedLongWords = {"hello", "world"}; EXPECT_EQ(longWordsResult, expectedLongWords); } @@ -739,11 +739,11 @@ TEST_F(ContainerTraitsTest, ContainerPipeWithDifferentTypes) { TEST_F(ContainerTraitsTest, EmptyContainerTests) { std::vector emptyVector; auto emptyPipe = atom::meta::make_container_pipe(emptyVector); - + // Transform on empty container should return empty container auto transformedEmpty = emptyPipe.transform([](int x) { return x * 2; }); EXPECT_TRUE(transformedEmpty.get().empty()); - + // Filter on empty container should return empty container auto filteredEmpty = emptyPipe.filter([](int x) { return x > 0; }); EXPECT_TRUE(filteredEmpty.get().empty()); @@ -752,16 +752,16 @@ TEST_F(ContainerTraitsTest, EmptyContainerTests) { // Test with single element containers TEST_F(ContainerTraitsTest, SingleElementContainerTests) { std::vector singleElement = {42}; - + auto transformed = atom::meta::make_container_pipe(singleElement) .transform([](int x) { return x / 2; }); std::vector expected = {21}; EXPECT_EQ(transformed.get(), expected); - + auto filtered = atom::meta::make_container_pipe(singleElement) .filter([](int x) { return x > 50; }); EXPECT_TRUE(filtered.get().empty()); - + auto notFiltered = atom::meta::make_container_pipe(singleElement) .filter([](int x) { return x > 10; }); EXPECT_EQ(notFiltered.get(), singleElement); @@ -771,11 +771,11 @@ TEST_F(ContainerTraitsTest, SingleElementContainerTests) { TEST_F(ContainerTraitsTest, ComplexTypeTests) { using ComplexMap = std::map>; using ComplexMapTraits = atom::meta::ContainerTraits; - + EXPECT_TRUE(ComplexMapTraits::is_associative_container); EXPECT_TRUE(ComplexMapTraits::has_key_type); EXPECT_TRUE(ComplexMapTraits::has_mapped_type); - + static_assert(std::is_same_v); static_assert(std::is_same_v>); } @@ -784,9 +784,9 @@ TEST_F(ContainerTraitsTest, ComplexTypeTests) { TEST_F(ContainerTraitsTest, OperationDetection) { // Test container_supports_operation (basic test since it's a SFINAE helper) using VectorSupportsOp = atom::meta::container_supports_operation< - std::vector, + std::vector, void(typename atom::meta::ContainerTraits>::value_type)>; - + // This tests the SFINAE mechanism - exact test depends on the specific operation signature // The test mainly ensures the template compiles correctly static_assert(std::is_same_v); @@ -794,4 +794,4 @@ TEST_F(ContainerTraitsTest, OperationDetection) { } // namespace atom::test -#endif // ATOM_TEST_CONTAINER_TRAITS_HPP \ No newline at end of file +#endif // ATOM_TEST_CONTAINER_TRAITS_HPP diff --git a/tests/meta/test_conversion.hpp b/tests/meta/test_conversion.hpp index ad830565..0c6f4851 100644 --- a/tests/meta/test_conversion.hpp +++ b/tests/meta/test_conversion.hpp @@ -441,4 +441,4 @@ TEST_F(ConversionTest, ReferenceConversions) { } // namespace atom::test -#endif // ATOM_TEST_CONVERSION_HPP \ No newline at end of file +#endif // ATOM_TEST_CONVERSION_HPP diff --git a/tests/meta/test_decorate.cpp b/tests/meta/test_decorate.cpp index f78d555f..5903b39a 100644 --- a/tests/meta/test_decorate.cpp +++ b/tests/meta/test_decorate.cpp @@ -557,4 +557,4 @@ TEST_F(DecorateTest, ConceptsAndTypeTraits) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/meta/test_enum.hpp b/tests/meta/test_enum.hpp index 372675bc..83d7f12f 100644 --- a/tests/meta/test_enum.hpp +++ b/tests/meta/test_enum.hpp @@ -71,21 +71,21 @@ struct EnumTraits { } }; -// Complete EnumTraits specialization for Permissions (as flag enum) +// Complete EnumTraits specialization for Permissions (as flag enum) template <> struct EnumTraits { using enum_type = test::Permissions; using underlying_type = std::underlying_type_t; static constexpr std::array values = { - test::Permissions::None, test::Permissions::Read, test::Permissions::Write, + test::Permissions::None, test::Permissions::Read, test::Permissions::Write, test::Permissions::Execute, test::Permissions::All}; static constexpr std::array names = { "None", "Read", "Write", "Execute", "All"}; static constexpr std::array descriptions = { - "No permissions", "Read permission", "Write permission", + "No permissions", "Read permission", "Write permission", "Execute permission", "All permissions"}; static constexpr std::array aliases = { @@ -409,10 +409,10 @@ TEST_F(EnumTest, FlagEnumFunctions) { // Test get_set_flags function TEST_F(EnumTest, GetSetFlags) { Permissions readWrite = Permissions::Read | Permissions::Write; - + auto setFlags = atom::meta::get_set_flags(readWrite); EXPECT_EQ(setFlags.size(), 2); - + // Flags should be in the order they appear in the enum values array bool foundRead = false, foundWrite = false; for (const auto& flag : setFlags) { @@ -441,7 +441,7 @@ TEST_F(EnumTest, FlagSerialization) { // Test serializing combined flags Permissions readWrite = Permissions::Read | Permissions::Write; std::string readWriteStr = atom::meta::serialize_flags(readWrite); - + // Should contain both flag names separated by | EXPECT_TRUE(readWriteStr.find("Read") != std::string::npos); EXPECT_TRUE(readWriteStr.find("Write") != std::string::npos); @@ -495,8 +495,8 @@ TEST_F(EnumTest, FlagDeserialization) { TEST_F(EnumTest, EnumValidator) { // Create validator that only allows primary colors atom::meta::EnumValidator primaryColorValidator( - [](Color c) { - return c == Color::Red || c == Color::Green || c == Color::Blue; + [](Color c) { + return c == Color::Red || c == Color::Green || c == Color::Blue; }, "Only primary colors allowed" ); @@ -546,7 +546,7 @@ TEST_F(EnumTest, EnumIteratorAndRange) { for (auto color : atom::meta::enum_range()) { colors.push_back(color); } - + EXPECT_EQ(colors.size(), 4); EXPECT_EQ(colors[0], Color::Red); EXPECT_EQ(colors[1], Color::Green); @@ -683,4 +683,4 @@ TEST_F(EnumTest, IntegerInEnumRange) { } // namespace atom::test -#endif // ATOM_TEST_ENUM_HPP \ No newline at end of file +#endif // ATOM_TEST_ENUM_HPP diff --git a/tests/meta/test_func_traits.hpp b/tests/meta/test_func_traits.hpp index d531f69a..9e00dad7 100644 --- a/tests/meta/test_func_traits.hpp +++ b/tests/meta/test_func_traits.hpp @@ -396,4 +396,4 @@ TEST_F(FunctionTraitsTest, HasConstMethodDetection) { } // namespace atom::test -#endif // ATOM_TEST_FUNC_TRAITS_HPP \ No newline at end of file +#endif // ATOM_TEST_FUNC_TRAITS_HPP diff --git a/tests/meta/test_global_ptr.hpp b/tests/meta/test_global_ptr.hpp index 33589abf..fb2fb4e6 100644 --- a/tests/meta/test_global_ptr.hpp +++ b/tests/meta/test_global_ptr.hpp @@ -577,4 +577,4 @@ TEST_F(GlobalPtrTest, GetWeakPtrMacroSimulated) { } // namespace atom::test -#endif // ATOM_TEST_GLOBAL_PTR_HPP \ No newline at end of file +#endif // ATOM_TEST_GLOBAL_PTR_HPP diff --git a/tests/meta/test_god.hpp b/tests/meta/test_god.hpp index 0b8e807d..cdde3ecc 100644 --- a/tests/meta/test_god.hpp +++ b/tests/meta/test_god.hpp @@ -710,4 +710,4 @@ TEST_F(GodTest, AtomicThreadSafetyTest) { EXPECT_EQ(counter.load(), kNumThreads * kIterationsPerThread); } -} // namespace atom::meta::test \ No newline at end of file +} // namespace atom::meta::test diff --git a/tests/meta/test_invoke.hpp b/tests/meta/test_invoke.hpp index d35e141c..82e667d1 100644 --- a/tests/meta/test_invoke.hpp +++ b/tests/meta/test_invoke.hpp @@ -613,4 +613,4 @@ TEST(FunctionCallInfoTest, BasicFunctionality) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/meta/test_member.cpp b/tests/meta/test_member.cpp index 2562ceaf..cdcd290f 100644 --- a/tests/meta/test_member.cpp +++ b/tests/meta/test_member.cpp @@ -449,4 +449,4 @@ TEST_F(MemberTest, ErrorHandling) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/meta/test_overload.hpp b/tests/meta/test_overload.hpp index ed55e7a5..3cbdcc99 100644 --- a/tests/meta/test_overload.hpp +++ b/tests/meta/test_overload.hpp @@ -388,4 +388,4 @@ TEST_F(OverloadTest, EdgeCases) { } // namespace atom::meta::test -#endif // ATOM_META_TEST_OVERLOAD_HPP \ No newline at end of file +#endif // ATOM_META_TEST_OVERLOAD_HPP diff --git a/tests/meta/test_property.hpp b/tests/meta/test_property.hpp index 662c0a5c..2a4b75d0 100644 --- a/tests/meta/test_property.hpp +++ b/tests/meta/test_property.hpp @@ -534,4 +534,4 @@ int main(int argc, char** argv) { return RUN_ALL_TESTS(); } -#endif // ATOM_META_TEST_PROPERTY_HPP \ No newline at end of file +#endif // ATOM_META_TEST_PROPERTY_HPP diff --git a/tests/meta/test_proxy.hpp b/tests/meta/test_proxy.hpp index 3b0a3c75..5c8a4572 100644 --- a/tests/meta/test_proxy.hpp +++ b/tests/meta/test_proxy.hpp @@ -648,4 +648,4 @@ TEST(FactoryFunctionTest, ProxyFactoryFunctions) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/meta/test_proxy_params.hpp b/tests/meta/test_proxy_params.hpp index 2654ef6f..fda83605 100644 --- a/tests/meta/test_proxy_params.hpp +++ b/tests/meta/test_proxy_params.hpp @@ -638,4 +638,4 @@ TEST_F(FunctionParamsTest, ComplexUsageScenarios) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/meta/test_raw_name.hpp b/tests/meta/test_raw_name.hpp index d0e1add6..8f7f7350 100644 --- a/tests/meta/test_raw_name.hpp +++ b/tests/meta/test_raw_name.hpp @@ -276,4 +276,4 @@ int main(int argc, char** argv) { return RUN_ALL_TESTS(); } -#endif // ATOM_META_TEST_RAW_NAME_HPP \ No newline at end of file +#endif // ATOM_META_TEST_RAW_NAME_HPP diff --git a/tests/meta/test_signature.cpp b/tests/meta/test_signature.cpp index 3c99bff7..2799ed71 100644 --- a/tests/meta/test_signature.cpp +++ b/tests/meta/test_signature.cpp @@ -460,4 +460,4 @@ TEST_F(SignatureTest, ParameterComparison) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/meta/test_stepper.hpp b/tests/meta/test_stepper.hpp index b12a783c..2b1830ce 100644 --- a/tests/meta/test_stepper.hpp +++ b/tests/meta/test_stepper.hpp @@ -749,4 +749,4 @@ TEST_F(FunctionSequenceTest, StatisticsAndDiagnostics) { EXPECT_EQ(stats.cacheMisses, 0); } -} // namespace atom::test \ No newline at end of file +} // namespace atom::test diff --git a/tests/meta/test_template_traits.hpp b/tests/meta/test_template_traits.hpp index 46767a0b..296a6913 100644 --- a/tests/meta/test_template_traits.hpp +++ b/tests/meta/test_template_traits.hpp @@ -596,4 +596,4 @@ TEST_F(TemplateTraitsTest, StaticDiagnosticsTests) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/meta/test_type_info.hpp b/tests/meta/test_type_info.hpp index 019ebb80..7276c663 100644 --- a/tests/meta/test_type_info.hpp +++ b/tests/meta/test_type_info.hpp @@ -463,4 +463,4 @@ TEST_F(TypeInfoTest, RegisterCustomTypeInfo) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/meta/test_vany.hpp b/tests/meta/test_vany.hpp index 1d4a973a..a91ce701 100644 --- a/tests/meta/test_vany.hpp +++ b/tests/meta/test_vany.hpp @@ -625,4 +625,4 @@ TEST_F(AnyTest, TypeInfo) { } // namespace atom::meta::test -#endif // ATOM_META_TEST_VANY_HPP \ No newline at end of file +#endif // ATOM_META_TEST_VANY_HPP diff --git a/tests/search/test_lru.hpp b/tests/search/test_lru.hpp index d4a9b1da..936c4c39 100644 --- a/tests/search/test_lru.hpp +++ b/tests/search/test_lru.hpp @@ -486,4 +486,4 @@ TEST_F(ThreadSafeLRUCacheTest, AccessOrder) { EXPECT_FALSE(cache->get("key2").has_value()); // Should be evicted EXPECT_TRUE(cache->get("key3").has_value()); EXPECT_TRUE(cache->get("key4").has_value()); -} \ No newline at end of file +} diff --git a/tests/search/test_ttl.hpp b/tests/search/test_ttl.hpp index 3070cdc6..8ca58916 100644 --- a/tests/search/test_ttl.hpp +++ b/tests/search/test_ttl.hpp @@ -320,4 +320,4 @@ TEST_F(TTLCacheTest, StressTest) { for (int i = 0; i < 50; i++) { EXPECT_FALSE(stressCache->get(i).has_value()); } -} \ No newline at end of file +} diff --git a/tests/serial/test_bluetooth_serial.hpp b/tests/serial/test_bluetooth_serial.hpp index 96a22d6e..5007a447 100644 --- a/tests/serial/test_bluetooth_serial.hpp +++ b/tests/serial/test_bluetooth_serial.hpp @@ -468,4 +468,4 @@ TEST_F(BluetoothSerialTest, MoveSemantics) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/serial/test_scanner.cpp b/tests/serial/test_scanner.cpp index 4d8728f1..5daeebfd 100644 --- a/tests/serial/test_scanner.cpp +++ b/tests/serial/test_scanner.cpp @@ -280,4 +280,4 @@ TEST_F(SerialPortScannerTest, FullPortScanningWorkflow) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/serial/test_serial_port.hpp b/tests/serial/test_serial_port.hpp index 50261aa3..e4d8ff2d 100644 --- a/tests/serial/test_serial_port.hpp +++ b/tests/serial/test_serial_port.hpp @@ -54,7 +54,7 @@ class SerialPortTest : public ::testing::Test { protected: void SetUp() override { mockImpl = std::make_shared(); - + // Setup default config for testing config.baudRate = 115200; config.dataBits = 8; @@ -63,19 +63,19 @@ class SerialPortTest : public ::testing::Test { config.flowControl = SerialConfig::FlowControl::None; config.readTimeout = 500ms; config.writeTimeout = 500ms; - + // Setup test data testData = {0x01, 0x02, 0x03, 0x04, 0x05}; } - + void TearDown() override { // Clean up test resources if needed } - + std::shared_ptr mockImpl; SerialConfig config; std::vector testData; - const std::string testPort = + const std::string testPort = #ifdef _WIN32 "COM3"; #else @@ -88,34 +88,34 @@ TEST_F(SerialPortTest, OpenClosePort) { // Setup expectations EXPECT_CALL(*mockImpl, open(testPort, _)) .Times(1); - + EXPECT_CALL(*mockImpl, isOpen()) .WillOnce(Return(true)) .WillOnce(Return(false)); - + EXPECT_CALL(*mockImpl, close()) .Times(1); - + EXPECT_CALL(*mockImpl, getPortName()) .WillOnce(Return(testPort)); - + // Create a SerialPort with our mock implementation // Note: In a real test, you would need a way to inject the mock SerialPort port; // For testing purposes, we'll simulate the behavior as if the mock was injected - + // Open the port port.open(testPort, config); - + // Verify port is open EXPECT_TRUE(port.isOpen()); - + // Verify port name EXPECT_EQ(testPort, port.getPortName()); - + // Close the port port.close(); - + // Verify port is closed EXPECT_FALSE(port.isOpen()); } @@ -125,7 +125,7 @@ TEST_F(SerialPortTest, OpenInvalidPort) { // Setup expectations EXPECT_CALL(*mockImpl, open("invalid_port", _)) .WillOnce(Throw(SerialIOException("Failed to open port: Access denied"))); - + // Try to open an invalid port // Note: Since we're simulating behavior, we'll just verify the expectation was set try { @@ -141,36 +141,36 @@ TEST_F(SerialPortTest, ReadData) { // Setup expectations EXPECT_CALL(*mockImpl, isOpen()) .WillRepeatedly(Return(true)); - + EXPECT_CALL(*mockImpl, read(5)) .WillOnce(Return(testData)); - + EXPECT_CALL(*mockImpl, readExactly(3, 1000ms)) .WillOnce(Return(std::vector{0x01, 0x02, 0x03})); - + EXPECT_CALL(*mockImpl, readAvailable()) .WillOnce(Return(testData)); - + EXPECT_CALL(*mockImpl, available()) .WillOnce(Return(5)); - + // Test regular read auto data = mockImpl->read(5); ASSERT_EQ(data.size(), 5); EXPECT_EQ(data, testData); - + // Test reading exactly N bytes auto exactData = mockImpl->readExactly(3, 1000ms); ASSERT_EQ(exactData.size(), 3); EXPECT_EQ(exactData[0], 0x01); EXPECT_EQ(exactData[1], 0x02); EXPECT_EQ(exactData[2], 0x03); - + // Test reading all available data auto availData = mockImpl->readAvailable(); ASSERT_EQ(availData.size(), 5); EXPECT_EQ(availData, testData); - + // Test checking bytes available size_t bytesAvailable = mockImpl->available(); EXPECT_EQ(bytesAvailable, 5); @@ -181,13 +181,13 @@ TEST_F(SerialPortTest, ReadFromClosedPort) { // Setup expectations EXPECT_CALL(*mockImpl, isOpen()) .WillRepeatedly(Return(false)); - + EXPECT_CALL(*mockImpl, read(_)) .WillOnce(Throw(SerialPortNotOpenException())); - + EXPECT_CALL(*mockImpl, readAvailable()) .WillOnce(Throw(SerialPortNotOpenException())); - + // Test reading from a closed port try { mockImpl->read(5); @@ -195,7 +195,7 @@ TEST_F(SerialPortTest, ReadFromClosedPort) { } catch (const SerialPortNotOpenException& e) { EXPECT_STREQ("Port not open", e.what()); } - + // Test reading available from a closed port try { mockImpl->readAvailable(); @@ -210,10 +210,10 @@ TEST_F(SerialPortTest, ReadTimeout) { // Setup expectations EXPECT_CALL(*mockImpl, isOpen()) .WillRepeatedly(Return(true)); - + EXPECT_CALL(*mockImpl, readExactly(10, _)) .WillOnce(Throw(SerialTimeoutException())); - + // Test read timeout try { mockImpl->readExactly(10, 500ms); @@ -229,11 +229,11 @@ TEST_F(SerialPortTest, AsyncRead) { std::atomic dataReceived{false}; std::mutex mutex; std::condition_variable cv; - + // Setup expectations EXPECT_CALL(*mockImpl, isOpen()) .WillRepeatedly(Return(true)); - + EXPECT_CALL(*mockImpl, asyncRead(_, _)) .WillOnce([this, &receivedData, &dataReceived, &cv](size_t maxBytes, auto callback) { // Simulate async read by calling the callback with test data @@ -244,18 +244,18 @@ TEST_F(SerialPortTest, AsyncRead) { cv.notify_one(); }).detach(); }); - + // Start async read mockImpl->asyncRead(10, [&receivedData](std::vector data) { receivedData = std::move(data); }); - + // Wait for async read to complete { std::unique_lock lock(mutex); cv.wait_for(lock, 5s, [&dataReceived]() { return dataReceived.load(); }); } - + // Verify received data ASSERT_TRUE(dataReceived.load()); ASSERT_EQ(receivedData.size(), 5); @@ -267,30 +267,30 @@ TEST_F(SerialPortTest, WriteData) { // Setup expectations EXPECT_CALL(*mockImpl, isOpen()) .WillRepeatedly(Return(true)); - + EXPECT_CALL(*mockImpl, write(std::span(testData))) .WillOnce(Return(5)); - + EXPECT_CALL(*mockImpl, write(std::string("Hello Serial"))) .WillOnce(Return(12)); - + EXPECT_CALL(*mockImpl, flush()) .Times(1); - + EXPECT_CALL(*mockImpl, drain()) .Times(1); - + // Test writing binary data size_t bytesWritten = mockImpl->write(std::span(testData)); EXPECT_EQ(bytesWritten, 5); - + // Test writing string data bytesWritten = mockImpl->write(std::string("Hello Serial")); EXPECT_EQ(bytesWritten, 12); - + // Test flush mockImpl->flush(); - + // Test drain mockImpl->drain(); } @@ -300,10 +300,10 @@ TEST_F(SerialPortTest, WriteToClosedPort) { // Setup expectations EXPECT_CALL(*mockImpl, isOpen()) .WillRepeatedly(Return(false)); - + EXPECT_CALL(*mockImpl, write(std::span(_))) .WillOnce(Throw(SerialPortNotOpenException())); - + // Test writing to a closed port try { mockImpl->write(std::span(testData)); @@ -318,10 +318,10 @@ TEST_F(SerialPortTest, WriteTimeout) { // Setup expectations EXPECT_CALL(*mockImpl, isOpen()) .WillRepeatedly(Return(true)); - + EXPECT_CALL(*mockImpl, write(std::span(_))) .WillOnce(Throw(SerialTimeoutException())); - + // Test write timeout try { mockImpl->write(std::span(testData)); @@ -336,19 +336,19 @@ TEST_F(SerialPortTest, Configuration) { // Setup expectations EXPECT_CALL(*mockImpl, isOpen()) .WillRepeatedly(Return(true)); - + EXPECT_CALL(*mockImpl, setConfig(_)) .Times(1); - + EXPECT_CALL(*mockImpl, getConfig()) .WillOnce(Return(config)); - + // Set configuration mockImpl->setConfig(config); - + // Get configuration auto retrievedConfig = mockImpl->getConfig(); - + // Verify configuration EXPECT_EQ(retrievedConfig.baudRate, 115200); EXPECT_EQ(retrievedConfig.dataBits, 8); @@ -364,43 +364,43 @@ TEST_F(SerialPortTest, SignalControl) { // Setup expectations EXPECT_CALL(*mockImpl, isOpen()) .WillRepeatedly(Return(true)); - + EXPECT_CALL(*mockImpl, setDTR(true)) .Times(1); - + EXPECT_CALL(*mockImpl, setRTS(false)) .Times(1); - + EXPECT_CALL(*mockImpl, getCTS()) .WillOnce(Return(true)); - + EXPECT_CALL(*mockImpl, getDSR()) .WillOnce(Return(false)); - + EXPECT_CALL(*mockImpl, getRI()) .WillOnce(Return(false)); - + EXPECT_CALL(*mockImpl, getCD()) .WillOnce(Return(true)); - + // Set DTR mockImpl->setDTR(true); - + // Set RTS mockImpl->setRTS(false); - + // Get CTS bool cts = mockImpl->getCTS(); EXPECT_TRUE(cts); - + // Get DSR bool dsr = mockImpl->getDSR(); EXPECT_FALSE(dsr); - + // Get RI bool ri = mockImpl->getRI(); EXPECT_FALSE(ri); - + // Get CD bool cd = mockImpl->getCD(); EXPECT_TRUE(cd); @@ -416,13 +416,13 @@ TEST_F(SerialPortTest, AvailablePorts) { "/dev/ttyS0", "/dev/ttyUSB0", "/dev/ttyACM0" #endif }; - + EXPECT_CALL(*mockImpl, getAvailablePorts()) .WillOnce(Return(availablePorts)); - + // Get available ports auto ports = mockImpl->getAvailablePorts(); - + // Verify ports ASSERT_EQ(ports.size(), 3); #ifdef _WIN32 @@ -441,15 +441,15 @@ TEST_F(SerialPortTest, Exceptions) { // Test base SerialException SerialException baseEx("Base serial exception"); EXPECT_STREQ("Base serial exception", baseEx.what()); - + // Test SerialPortNotOpenException SerialPortNotOpenException notOpenEx; EXPECT_STREQ("Port not open", notOpenEx.what()); - + // Test SerialTimeoutException SerialTimeoutException timeoutEx; EXPECT_STREQ("Serial operation timed out", timeoutEx.what()); - + // Test SerialIOException SerialIOException ioEx("I/O error: permission denied"); EXPECT_STREQ("I/O error: permission denied", ioEx.what()); @@ -459,24 +459,24 @@ TEST_F(SerialPortTest, Exceptions) { TEST_F(SerialPortTest, MoveSemantics) { // This would be tested with real SerialPort instances, not with mocks // Here we document how it could be tested - + // Create first SerialPort SerialPort port1; // Open a port // port1.open(testPort); - + // Move-construct a second port // SerialPort port2(std::move(port1)); - + // Verify port2 is now connected and port1 is in a valid but unspecified state // EXPECT_TRUE(port2.isOpen()); - + // Create another port // SerialPort port3; - + // Move-assign from port2 // port3 = std::move(port2); - + // Verify port3 is now connected and port2 is in a valid but unspecified state // EXPECT_TRUE(port3.isOpen()); } @@ -486,13 +486,13 @@ TEST_F(SerialPortTest, IOErrors) { // Setup expectations EXPECT_CALL(*mockImpl, isOpen()) .WillRepeatedly(Return(true)); - + EXPECT_CALL(*mockImpl, read(_)) .WillOnce(Throw(SerialIOException("Hardware error: device disconnected"))); - + EXPECT_CALL(*mockImpl, write(std::span(_))) .WillOnce(Throw(SerialIOException("Write error: device disconnected"))); - + // Test I/O error during read try { mockImpl->read(5); @@ -500,7 +500,7 @@ TEST_F(SerialPortTest, IOErrors) { } catch (const SerialIOException& e) { EXPECT_STREQ("Hardware error: device disconnected", e.what()); } - + // Test I/O error during write try { mockImpl->write(std::span(testData)); @@ -515,14 +515,14 @@ TEST_F(SerialPortTest, ConfigurationErrors) { // Setup expectations EXPECT_CALL(*mockImpl, isOpen()) .WillRepeatedly(Return(true)); - + // Invalid baud rate SerialConfig invalidConfig = config; invalidConfig.baudRate = -1; - + EXPECT_CALL(*mockImpl, setConfig(invalidConfig)) .WillOnce(Throw(SerialIOException("Invalid configuration: baud rate out of range"))); - + // Test configuration error try { mockImpl->setConfig(invalidConfig); @@ -537,17 +537,17 @@ TEST_F(SerialPortTest, ZeroLengthOperations) { // Setup expectations EXPECT_CALL(*mockImpl, isOpen()) .WillRepeatedly(Return(true)); - + EXPECT_CALL(*mockImpl, read(0)) .WillOnce(Return(std::vector{})); - + EXPECT_CALL(*mockImpl, write(std::span(std::vector{}))) .WillOnce(Return(0)); - + // Test zero-length read auto emptyRead = mockImpl->read(0); EXPECT_TRUE(emptyRead.empty()); - + // Test zero-length write std::vector emptyData; size_t bytesWritten = mockImpl->write(std::span(emptyData)); @@ -559,4 +559,4 @@ TEST_F(SerialPortTest, ZeroLengthOperations) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/serial/test_usb.cpp b/tests/serial/test_usb.cpp index b68c7e16..6c0a240f 100644 --- a/tests/serial/test_usb.cpp +++ b/tests/serial/test_usb.cpp @@ -554,4 +554,4 @@ TEST_F(UsbContextTest, HotplugRegistrationFailure) { EXPECT_THROW(context.startHotplugDetection(handler), UsbException); } -#endif // ATOM_SERIAL_TEST_USB_HPP \ No newline at end of file +#endif // ATOM_SERIAL_TEST_USB_HPP diff --git a/tests/system/test_command.cpp b/tests/system/test_command.cpp index beaa53b5..8216a839 100644 --- a/tests/system/test_command.cpp +++ b/tests/system/test_command.cpp @@ -476,4 +476,4 @@ TEST_F(CommandEdgeCaseTest, VeryLongCommand) { } } -} // namespace atom::system::test \ No newline at end of file +} // namespace atom::system::test diff --git a/tests/system/test_crash_quotes.cpp b/tests/system/test_crash_quotes.cpp index d05e26e0..a7fad78b 100644 --- a/tests/system/test_crash_quotes.cpp +++ b/tests/system/test_crash_quotes.cpp @@ -441,4 +441,4 @@ TEST(QuoteManagerPerformance, DISABLED_LargeCollection) { std::cout << "Time to filter 10000 quotes by author: " << duration << "ms" << std::endl; EXPECT_EQ(100, byAuthor.size()); -} \ No newline at end of file +} diff --git a/tests/system/test_env.hpp b/tests/system/test_env.hpp index 6aae2ee6..2719bc72 100644 --- a/tests/system/test_env.hpp +++ b/tests/system/test_env.hpp @@ -484,4 +484,4 @@ TEST_F(EnvTest, PrintAllArgs) { EXPECT_FALSE(output.empty()); EXPECT_TRUE(output.find("key1") != std::string::npos); } -#endif \ No newline at end of file +#endif diff --git a/tests/system/test_gpio.hpp b/tests/system/test_gpio.hpp index 7ada8d82..d365d96f 100644 --- a/tests/system/test_gpio.hpp +++ b/tests/system/test_gpio.hpp @@ -465,4 +465,4 @@ TEST_F(GPIOTest, AsyncOperation) { // 验证结果 EXPECT_TRUE(result); -} \ No newline at end of file +} diff --git a/tests/system/test_lregistry.hpp b/tests/system/test_lregistry.hpp index 4f94c509..621282ab 100644 --- a/tests/system/test_lregistry.hpp +++ b/tests/system/test_lregistry.hpp @@ -26,10 +26,10 @@ class RegistryTest : public ::testing::Test { std::string testFilePath = (tempDir / "test_registry.dat").string(); testBackupPath = (tempDir / "test_registry_backup.dat").string(); testExportPath = (tempDir / "test_registry_export.dat").string(); - + // Initialize registry ASSERT_EQ(registry.initialize(testFilePath), RegistryResult::SUCCESS); - + // Create a test key for most tests ASSERT_EQ(registry.createKey(testKeyPath), RegistryResult::SUCCESS); } @@ -38,15 +38,15 @@ class RegistryTest : public ::testing::Test { // Clean up test files auto tempDir = std::filesystem::temp_directory_path(); std::string testFilePath = (tempDir / "test_registry.dat").string(); - + if (std::filesystem::exists(testFilePath)) { std::filesystem::remove(testFilePath); } - + if (std::filesystem::exists(testBackupPath)) { std::filesystem::remove(testBackupPath); } - + if (std::filesystem::exists(testExportPath)) { std::filesystem::remove(testExportPath); } @@ -58,11 +58,11 @@ TEST_F(RegistryTest, CreateKey) { // 测试创建新键 EXPECT_EQ(registry.createKey("NewKey"), RegistryResult::SUCCESS); EXPECT_TRUE(registry.keyExists("NewKey")); - + // 测试创建嵌套键 EXPECT_EQ(registry.createKey("Parent/Child/GrandChild"), RegistryResult::SUCCESS); EXPECT_TRUE(registry.keyExists("Parent/Child/GrandChild")); - + // 测试创建已存在的键 EXPECT_EQ(registry.createKey(testKeyPath), RegistryResult::ALREADY_EXISTS); } @@ -71,14 +71,14 @@ TEST_F(RegistryTest, DeleteKey) { // 创建要删除的键 ASSERT_EQ(registry.createKey("KeyToDelete"), RegistryResult::SUCCESS); EXPECT_TRUE(registry.keyExists("KeyToDelete")); - + // 测试删除键 EXPECT_EQ(registry.deleteKey("KeyToDelete"), RegistryResult::SUCCESS); EXPECT_FALSE(registry.keyExists("KeyToDelete")); - + // 测试删除不存在的键 EXPECT_EQ(registry.deleteKey("NonExistentKey"), RegistryResult::KEY_NOT_FOUND); - + // 测试删除有子键的键 ASSERT_EQ(registry.createKey("Parent/Child"), RegistryResult::SUCCESS); EXPECT_EQ(registry.deleteKey("Parent"), RegistryResult::SUCCESS); @@ -89,10 +89,10 @@ TEST_F(RegistryTest, DeleteKey) { TEST_F(RegistryTest, KeyExists) { // 已存在的键 EXPECT_TRUE(registry.keyExists(testKeyPath)); - + // 不存在的键 EXPECT_FALSE(registry.keyExists("NonExistentKey")); - + // 删除后的键 registry.deleteKey(testKeyPath); EXPECT_FALSE(registry.keyExists(testKeyPath)); @@ -103,16 +103,16 @@ TEST_F(RegistryTest, GetAllKeys) { registry.createKey("Key1"); registry.createKey("Key2"); registry.createKey("Key3/SubKey"); - + // 获取所有键 auto keys = registry.getAllKeys(); - + // 验证结果包含所有创建的键 EXPECT_THAT(keys, testing::Contains(testKeyPath)); EXPECT_THAT(keys, testing::Contains("Key1")); EXPECT_THAT(keys, testing::Contains("Key2")); EXPECT_THAT(keys, testing::Contains("Key3/SubKey")); - + // 删除键后重新验证 registry.deleteKey("Key1"); keys = registry.getAllKeys(); @@ -122,20 +122,20 @@ TEST_F(RegistryTest, GetAllKeys) { // 值操作测试 TEST_F(RegistryTest, SetAndGetValue) { // 设置一个普通值 - EXPECT_EQ(registry.setValue(testKeyPath, testValueName, testValueData), + EXPECT_EQ(registry.setValue(testKeyPath, testValueName, testValueData), RegistryResult::SUCCESS); - + // 获取该值 auto value = registry.getValue(testKeyPath, testValueName); EXPECT_TRUE(value.has_value()); EXPECT_EQ(value.value(), testValueData); - + // 设置不存在键的值 - EXPECT_EQ(registry.setValue("NonExistentKey", testValueName, testValueData), + EXPECT_EQ(registry.setValue("NonExistentKey", testValueName, testValueData), RegistryResult::KEY_NOT_FOUND); - + // 用空值覆盖现有值 - EXPECT_EQ(registry.setValue(testKeyPath, testValueName, ""), + EXPECT_EQ(registry.setValue(testKeyPath, testValueName, ""), RegistryResult::SUCCESS); value = registry.getValue(testKeyPath, testValueName); EXPECT_TRUE(value.has_value()); @@ -144,18 +144,18 @@ TEST_F(RegistryTest, SetAndGetValue) { TEST_F(RegistryTest, SetAndGetTypedValue) { // 设置一个带类型的值 - EXPECT_EQ(registry.setTypedValue(testKeyPath, testValueName, testValueData, "string"), + EXPECT_EQ(registry.setTypedValue(testKeyPath, testValueName, testValueData, "string"), RegistryResult::SUCCESS); - + // 获取该值及其类型 std::string type; auto value = registry.getTypedValue(testKeyPath, testValueName, type); EXPECT_TRUE(value.has_value()); EXPECT_EQ(value.value(), testValueData); EXPECT_EQ(type, "string"); - + // 设置不同类型的值 - EXPECT_EQ(registry.setTypedValue(testKeyPath, "IntValue", "42", "int"), + EXPECT_EQ(registry.setTypedValue(testKeyPath, "IntValue", "42", "int"), RegistryResult::SUCCESS); value = registry.getTypedValue(testKeyPath, "IntValue", type); EXPECT_TRUE(value.has_value()); @@ -165,35 +165,35 @@ TEST_F(RegistryTest, SetAndGetTypedValue) { TEST_F(RegistryTest, DeleteValue) { // 设置一个值然后删除它 - ASSERT_EQ(registry.setValue(testKeyPath, testValueName, testValueData), + ASSERT_EQ(registry.setValue(testKeyPath, testValueName, testValueData), RegistryResult::SUCCESS); EXPECT_TRUE(registry.valueExists(testKeyPath, testValueName)); - - EXPECT_EQ(registry.deleteValue(testKeyPath, testValueName), + + EXPECT_EQ(registry.deleteValue(testKeyPath, testValueName), RegistryResult::SUCCESS); EXPECT_FALSE(registry.valueExists(testKeyPath, testValueName)); - + // 删除不存在的值 - EXPECT_EQ(registry.deleteValue(testKeyPath, "NonExistentValue"), + EXPECT_EQ(registry.deleteValue(testKeyPath, "NonExistentValue"), RegistryResult::VALUE_NOT_FOUND); - + // 从不存在的键中删除值 - EXPECT_EQ(registry.deleteValue("NonExistentKey", testValueName), + EXPECT_EQ(registry.deleteValue("NonExistentKey", testValueName), RegistryResult::KEY_NOT_FOUND); } TEST_F(RegistryTest, ValueExists) { // 检查不存在的值 EXPECT_FALSE(registry.valueExists(testKeyPath, testValueName)); - + // 设置值 registry.setValue(testKeyPath, testValueName, testValueData); EXPECT_TRUE(registry.valueExists(testKeyPath, testValueName)); - + // 删除值后再检查 registry.deleteValue(testKeyPath, testValueName); EXPECT_FALSE(registry.valueExists(testKeyPath, testValueName)); - + // 检查不存在键中的值 EXPECT_FALSE(registry.valueExists("NonExistentKey", testValueName)); } @@ -203,22 +203,22 @@ TEST_F(RegistryTest, GetValueNames) { registry.setValue(testKeyPath, "Value1", "Data1"); registry.setValue(testKeyPath, "Value2", "Data2"); registry.setValue(testKeyPath, "Value3", "Data3"); - + // 获取值名 auto valueNames = registry.getValueNames(testKeyPath); - + // 验证结果 EXPECT_THAT(valueNames, testing::Contains("Value1")); EXPECT_THAT(valueNames, testing::Contains("Value2")); EXPECT_THAT(valueNames, testing::Contains("Value3")); EXPECT_EQ(valueNames.size(), 3); - + // 删除一个值后再检查 registry.deleteValue(testKeyPath, "Value2"); valueNames = registry.getValueNames(testKeyPath); EXPECT_THAT(valueNames, testing::Not(testing::Contains("Value2"))); EXPECT_EQ(valueNames.size(), 2); - + // 检查不存在键的值名 valueNames = registry.getValueNames("NonExistentKey"); EXPECT_TRUE(valueNames.empty()); @@ -227,9 +227,9 @@ TEST_F(RegistryTest, GetValueNames) { TEST_F(RegistryTest, GetValueInfo) { // 设置一个带类型的值 std::string testType = "string"; - ASSERT_EQ(registry.setTypedValue(testKeyPath, testValueName, testValueData, testType), + ASSERT_EQ(registry.setTypedValue(testKeyPath, testValueName, testValueData, testType), RegistryResult::SUCCESS); - + // 获取值信息 auto valueInfo = registry.getValueInfo(testKeyPath, testValueName); EXPECT_TRUE(valueInfo.has_value()); @@ -239,11 +239,11 @@ TEST_F(RegistryTest, GetValueInfo) { // 我们不能严格测试lastModified的具体值,但可以确保它是近期时间 auto now = std::time(nullptr); EXPECT_LE(std::abs(std::difftime(valueInfo->lastModified, now)), 60); // 60秒内 - + // 获取不存在值的信息 valueInfo = registry.getValueInfo(testKeyPath, "NonExistentValue"); EXPECT_FALSE(valueInfo.has_value()); - + // 从不存在的键获取值信息 valueInfo = registry.getValueInfo("NonExistentKey", testValueName); EXPECT_FALSE(valueInfo.has_value()); @@ -252,128 +252,128 @@ TEST_F(RegistryTest, GetValueInfo) { // 文件操作测试 TEST_F(RegistryTest, LoadRegistryFromFile) { // 首先存储一些数据 - ASSERT_EQ(registry.setValue(testKeyPath, testValueName, testValueData), + ASSERT_EQ(registry.setValue(testKeyPath, testValueName, testValueData), RegistryResult::SUCCESS); - + // 获取测试文件路径 auto tempDir = std::filesystem::temp_directory_path(); std::string testFilePath = (tempDir / "test_registry.dat").string(); - + // 创建一个新的注册表实例 Registry newRegistry; - + // 加载文件 - EXPECT_EQ(newRegistry.loadRegistryFromFile(testFilePath), + EXPECT_EQ(newRegistry.loadRegistryFromFile(testFilePath), RegistryResult::SUCCESS); - + // 验证数据已正确加载 EXPECT_TRUE(newRegistry.keyExists(testKeyPath)); auto value = newRegistry.getValue(testKeyPath, testValueName); EXPECT_TRUE(value.has_value()); EXPECT_EQ(value.value(), testValueData); - + // 尝试加载不存在的文件 - EXPECT_EQ(newRegistry.loadRegistryFromFile("non_existent_file.dat"), + EXPECT_EQ(newRegistry.loadRegistryFromFile("non_existent_file.dat"), RegistryResult::FILE_ERROR); } TEST_F(RegistryTest, BackupAndRestoreRegistry) { // 设置测试数据 - ASSERT_EQ(registry.setValue(testKeyPath, testValueName, testValueData), + ASSERT_EQ(registry.setValue(testKeyPath, testValueName, testValueData), RegistryResult::SUCCESS); - + // 备份注册表 - EXPECT_EQ(registry.backupRegistryData(testBackupPath), + EXPECT_EQ(registry.backupRegistryData(testBackupPath), RegistryResult::SUCCESS); EXPECT_TRUE(std::filesystem::exists(testBackupPath)); - + // 修改注册表数据 - ASSERT_EQ(registry.setValue(testKeyPath, testValueName, "ModifiedData"), + ASSERT_EQ(registry.setValue(testKeyPath, testValueName, "ModifiedData"), RegistryResult::SUCCESS); auto value = registry.getValue(testKeyPath, testValueName); EXPECT_EQ(value.value(), "ModifiedData"); - + // 从备份恢复 - EXPECT_EQ(registry.restoreRegistryData(testBackupPath), + EXPECT_EQ(registry.restoreRegistryData(testBackupPath), RegistryResult::SUCCESS); - + // 验证数据已恢复 value = registry.getValue(testKeyPath, testValueName); EXPECT_TRUE(value.has_value()); EXPECT_EQ(value.value(), testValueData); - + // 尝试从不存在的文件恢复 - EXPECT_EQ(registry.restoreRegistryData("non_existent_backup.dat"), + EXPECT_EQ(registry.restoreRegistryData("non_existent_backup.dat"), RegistryResult::FILE_ERROR); } TEST_F(RegistryTest, ExportAndImportRegistry) { // 设置测试数据 - ASSERT_EQ(registry.setValue(testKeyPath, testValueName, testValueData), + ASSERT_EQ(registry.setValue(testKeyPath, testValueName, testValueData), RegistryResult::SUCCESS); - ASSERT_EQ(registry.setValue(testKeyPath, "AnotherValue", "AnotherData"), + ASSERT_EQ(registry.setValue(testKeyPath, "AnotherValue", "AnotherData"), RegistryResult::SUCCESS); - + // 导出为不同格式 for (auto format : {RegistryFormat::TEXT, RegistryFormat::JSON, RegistryFormat::XML}) { // 导出注册表 - EXPECT_EQ(registry.exportRegistry(testExportPath, format), + EXPECT_EQ(registry.exportRegistry(testExportPath, format), RegistryResult::SUCCESS); EXPECT_TRUE(std::filesystem::exists(testExportPath)); - + // 创建新的注册表实例 Registry importedRegistry; - ASSERT_EQ(importedRegistry.initialize(testExportPath + ".import"), + ASSERT_EQ(importedRegistry.initialize(testExportPath + ".import"), RegistryResult::SUCCESS); - + // 导入注册表 - EXPECT_EQ(importedRegistry.importRegistry(testExportPath, format), + EXPECT_EQ(importedRegistry.importRegistry(testExportPath, format), RegistryResult::SUCCESS); - + // 验证数据已正确导入 EXPECT_TRUE(importedRegistry.keyExists(testKeyPath)); auto value = importedRegistry.getValue(testKeyPath, testValueName); EXPECT_TRUE(value.has_value()); EXPECT_EQ(value.value(), testValueData); - + value = importedRegistry.getValue(testKeyPath, "AnotherValue"); EXPECT_TRUE(value.has_value()); EXPECT_EQ(value.value(), "AnotherData"); - + // 删除测试文件 std::filesystem::remove(testExportPath); std::filesystem::remove(testExportPath + ".import"); } - + // 测试导入同时合并现有数据 - ASSERT_EQ(registry.exportRegistry(testExportPath, RegistryFormat::JSON), + ASSERT_EQ(registry.exportRegistry(testExportPath, RegistryFormat::JSON), RegistryResult::SUCCESS); - + // 创建一个带有部分不同数据的新注册表 Registry mergeRegistry; - ASSERT_EQ(mergeRegistry.initialize(testExportPath + ".merge"), + ASSERT_EQ(mergeRegistry.initialize(testExportPath + ".merge"), RegistryResult::SUCCESS); - ASSERT_EQ(mergeRegistry.createKey("UniqueKey"), + ASSERT_EQ(mergeRegistry.createKey("UniqueKey"), RegistryResult::SUCCESS); - ASSERT_EQ(mergeRegistry.setValue("UniqueKey", "UniqueValue", "UniqueData"), + ASSERT_EQ(mergeRegistry.setValue("UniqueKey", "UniqueValue", "UniqueData"), RegistryResult::SUCCESS); - + // 导入并合并 - EXPECT_EQ(mergeRegistry.importRegistry(testExportPath, RegistryFormat::JSON, true), + EXPECT_EQ(mergeRegistry.importRegistry(testExportPath, RegistryFormat::JSON, true), RegistryResult::SUCCESS); - + // 验证原数据和导入的数据都存在 EXPECT_TRUE(mergeRegistry.keyExists("UniqueKey")); EXPECT_TRUE(mergeRegistry.keyExists(testKeyPath)); - + auto value = mergeRegistry.getValue("UniqueKey", "UniqueValue"); EXPECT_TRUE(value.has_value()); EXPECT_EQ(value.value(), "UniqueData"); - + value = mergeRegistry.getValue(testKeyPath, testValueName); EXPECT_TRUE(value.has_value()); EXPECT_EQ(value.value(), testValueData); - + // 删除测试文件 std::filesystem::remove(testExportPath); std::filesystem::remove(testExportPath + ".merge"); @@ -386,20 +386,20 @@ TEST_F(RegistryTest, SearchKeys) { registry.createKey("SearchTest/Key2"); registry.createKey("SearchTest/SubDir/Key3"); registry.createKey("DifferentPath/Key4"); - + // 使用模式搜索键 auto results = registry.searchKeys("SearchTest/*"); EXPECT_EQ(results.size(), 3); EXPECT_THAT(results, testing::Contains("SearchTest/Key1")); EXPECT_THAT(results, testing::Contains("SearchTest/Key2")); EXPECT_THAT(results, testing::Contains("SearchTest/SubDir/Key3")); - + // 使用更具体的模式 results = registry.searchKeys("SearchTest/Key*"); EXPECT_EQ(results.size(), 2); EXPECT_THAT(results, testing::Contains("SearchTest/Key1")); EXPECT_THAT(results, testing::Contains("SearchTest/Key2")); - + // 使用不匹配任何内容的模式 results = registry.searchKeys("NonExistent*"); EXPECT_TRUE(results.empty()); @@ -411,11 +411,11 @@ TEST_F(RegistryTest, SearchValues) { registry.setValue("SearchTest/Key2", "Value2", "DifferentContent"); registry.setValue("SearchTest/Key3", "Value3", "SearchableContentWithMore"); registry.setValue("DifferentPath/Key4", "Value4", "SearchableContent"); - + // 搜索特定内容的值 auto results = registry.searchValues("Searchable"); EXPECT_EQ(results.size(), 3); - + // 验证结果包含正确的键值对 bool foundKey1 = false, foundKey3 = false, foundKey4 = false; for (const auto& [key, value] : results) { @@ -430,11 +430,11 @@ TEST_F(RegistryTest, SearchValues) { EXPECT_TRUE(foundKey1); EXPECT_TRUE(foundKey3); EXPECT_TRUE(foundKey4); - + // 使用更具体的模式 results = registry.searchValues("SearchableContent$"); EXPECT_EQ(results.size(), 2); - + // 使用不匹配任何内容的模式 results = registry.searchValues("NonExistentPattern"); EXPECT_TRUE(results.empty()); @@ -444,7 +444,7 @@ TEST_F(RegistryTest, SearchValues) { TEST_F(RegistryTest, EventCallbacks) { bool callbackFired = false; std::string callbackKey, callbackValue; - + // 注册回调 size_t callbackId = registry.registerEventCallback( [&callbackFired, &callbackKey, &callbackValue](const std::string& key, const std::string& value) { @@ -452,25 +452,25 @@ TEST_F(RegistryTest, EventCallbacks) { callbackKey = key; callbackValue = value; }); - + // 触发回调 registry.setValue(testKeyPath, testValueName, testValueData); - + // 验证回调被调用 EXPECT_TRUE(callbackFired); EXPECT_EQ(callbackKey, testKeyPath); EXPECT_EQ(callbackValue, testValueName); - + // 重置标志 callbackFired = false; - + // 取消注册回调 EXPECT_TRUE(registry.unregisterEventCallback(callbackId)); - + // 确认回调不再被触发 registry.setValue(testKeyPath, "NewValue", "NewData"); EXPECT_FALSE(callbackFired); - + // 尝试取消注册不存在的回调 EXPECT_FALSE(registry.unregisterEventCallback(99999)); } @@ -479,34 +479,34 @@ TEST_F(RegistryTest, EventCallbacks) { TEST_F(RegistryTest, Transactions) { // 开始事务 EXPECT_TRUE(registry.beginTransaction()); - + // 在事务中进行一些更改 registry.setValue(testKeyPath, "TransactionValue1", "Data1"); registry.setValue(testKeyPath, "TransactionValue2", "Data2"); registry.createKey("TransactionKey"); - + // 回滚事务 EXPECT_EQ(registry.rollbackTransaction(), RegistryResult::SUCCESS); - + // 验证更改已被撤销 EXPECT_FALSE(registry.valueExists(testKeyPath, "TransactionValue1")); EXPECT_FALSE(registry.valueExists(testKeyPath, "TransactionValue2")); EXPECT_FALSE(registry.keyExists("TransactionKey")); - + // 再次开始事务 EXPECT_TRUE(registry.beginTransaction()); - + // 进行一些更改 registry.setValue(testKeyPath, "CommitValue", "CommitData"); registry.createKey("CommitKey"); - + // 提交事务 EXPECT_EQ(registry.commitTransaction(), RegistryResult::SUCCESS); - + // 验证更改已被保存 EXPECT_TRUE(registry.valueExists(testKeyPath, "CommitValue")); EXPECT_TRUE(registry.keyExists("CommitKey")); - + // 尝试在没有活动事务的情况下回滚 EXPECT_EQ(registry.rollbackTransaction(), RegistryResult::UNKNOWN_ERROR); } @@ -515,40 +515,40 @@ TEST_F(RegistryTest, Transactions) { TEST_F(RegistryTest, AutoSave) { // 设置自动保存 registry.setAutoSave(true); - + // 进行更改 registry.setValue(testKeyPath, "AutoSaveValue", "AutoSaveData"); - + // 创建新的注册表实例 Registry newRegistry; - + // 获取测试文件路径 auto tempDir = std::filesystem::temp_directory_path(); std::string testFilePath = (tempDir / "test_registry.dat").string(); - + // 尝试加载文件,验证数据是否已自动保存 - EXPECT_EQ(newRegistry.loadRegistryFromFile(testFilePath), + EXPECT_EQ(newRegistry.loadRegistryFromFile(testFilePath), RegistryResult::SUCCESS); - + // 验证数据已保存 EXPECT_TRUE(newRegistry.valueExists(testKeyPath, "AutoSaveValue")); auto value = newRegistry.getValue(testKeyPath, "AutoSaveValue"); EXPECT_TRUE(value.has_value()); EXPECT_EQ(value.value(), "AutoSaveData"); - + // 禁用自动保存 registry.setAutoSave(false); - + // 进行新的更改 registry.setValue(testKeyPath, "ManualSaveValue", "ManualSaveData"); - + // 创建另一个新的注册表实例 Registry anotherRegistry; - + // 加载文件 - EXPECT_EQ(anotherRegistry.loadRegistryFromFile(testFilePath), + EXPECT_EQ(anotherRegistry.loadRegistryFromFile(testFilePath), RegistryResult::SUCCESS); - + // 验证新更改未自动保存 EXPECT_FALSE(anotherRegistry.valueExists(testKeyPath, "ManualSaveValue")); } @@ -556,16 +556,16 @@ TEST_F(RegistryTest, AutoSave) { // 错误处理测试 TEST_F(RegistryTest, ErrorHandling) { // 尝试执行一个会失败的操作 - EXPECT_EQ(registry.setValue("NonExistentKey", testValueName, testValueData), + EXPECT_EQ(registry.setValue("NonExistentKey", testValueName, testValueData), RegistryResult::KEY_NOT_FOUND); - + // 获取上次操作的错误信息 std::string errorMsg = registry.getLastError(); EXPECT_FALSE(errorMsg.empty()); EXPECT_THAT(errorMsg, testing::HasSubstr("KEY_NOT_FOUND")); - + // 执行成功的操作后,错误信息应该被清除或更新 - EXPECT_EQ(registry.setValue(testKeyPath, testValueName, testValueData), + EXPECT_EQ(registry.setValue(testKeyPath, testValueName, testValueData), RegistryResult::SUCCESS); errorMsg = registry.getLastError(); EXPECT_TRUE(errorMsg.empty() || errorMsg.find("SUCCESS") != std::string::npos); @@ -575,46 +575,46 @@ TEST_F(RegistryTest, ErrorHandling) { TEST_F(RegistryTest, ThreadSafety) { const int numThreads = 5; const int operationsPerThread = 100; - + std::vector threads; - + // 启动多个线程同时读写注册表 for (int t = 0; t < numThreads; ++t) { threads.emplace_back([&, t]() { std::string threadKeyPath = "ThreadTest/Thread" + std::to_string(t); registry.createKey(threadKeyPath); - + for (int i = 0; i < operationsPerThread; ++i) { std::string valueName = "Value" + std::to_string(i); std::string valueData = "Data" + std::to_string(i) + "_" + std::to_string(t); - + registry.setValue(threadKeyPath, valueName, valueData); - + auto value = registry.getValue(threadKeyPath, valueName); if (value.has_value()) { EXPECT_EQ(value.value(), valueData); } - + if (i % 10 == 0) { registry.deleteValue(threadKeyPath, "Value" + std::to_string(i / 10)); } } }); } - + // 等待所有线程完成 for (auto& thread : threads) { thread.join(); } - + // 验证结果 for (int t = 0; t < numThreads; ++t) { std::string threadKeyPath = "ThreadTest/Thread" + std::to_string(t); EXPECT_TRUE(registry.keyExists(threadKeyPath)); - + auto valueNames = registry.getValueNames(threadKeyPath); EXPECT_FALSE(valueNames.empty()); - + // 验证一些随机值 for (int i = operationsPerThread - 5; i < operationsPerThread; ++i) { std::string valueName = "Value" + std::to_string(i); @@ -632,35 +632,35 @@ TEST_F(RegistryTest, ThreadSafety) { TEST_F(RegistryTest, DISABLED_PerformanceTest) { const int numKeys = 1000; const int valuesPerKey = 10; - + auto start = std::chrono::high_resolution_clock::now(); - + // 创建大量键和值 for (int i = 0; i < numKeys; ++i) { std::string keyPath = "PerfTest/Key" + std::to_string(i); registry.createKey(keyPath); - + for (int j = 0; j < valuesPerKey; ++j) { std::string valueName = "Value" + std::to_string(j); std::string valueData = "Data" + std::to_string(i) + "_" + std::to_string(j); registry.setValue(keyPath, valueName, valueData); } } - + auto createEnd = std::chrono::high_resolution_clock::now(); auto createDuration = std::chrono::duration_cast( createEnd - start).count(); - - std::cout << "Created " << numKeys << " keys with " << valuesPerKey + + std::cout << "Created " << numKeys << " keys with " << valuesPerKey << " values each in " << createDuration << "ms" << std::endl; - + // 读取所有值 int readCount = 0; start = std::chrono::high_resolution_clock::now(); - + for (int i = 0; i < numKeys; ++i) { std::string keyPath = "PerfTest/Key" + std::to_string(i); - + for (int j = 0; j < valuesPerKey; ++j) { std::string valueName = "Value" + std::to_string(j); auto value = registry.getValue(keyPath, valueName); @@ -669,20 +669,20 @@ TEST_F(RegistryTest, DISABLED_PerformanceTest) { } } } - + auto readEnd = std::chrono::high_resolution_clock::now(); auto readDuration = std::chrono::duration_cast( readEnd - start).count(); - + std::cout << "Read " << readCount << " values in " << readDuration << "ms" << std::endl; - + // 验证读取的数量正确 EXPECT_EQ(readCount, numKeys * valuesPerKey); - + // 输出每秒操作数 double createOpsPerSecond = (double)(numKeys * valuesPerKey) / ((double)createDuration / 1000.0); double readOpsPerSecond = (double)readCount / ((double)readDuration / 1000.0); - + std::cout << "Create operations per second: " << createOpsPerSecond << std::endl; std::cout << "Read operations per second: " << readOpsPerSecond << std::endl; } @@ -693,35 +693,35 @@ TEST_F(RegistryTest, EdgeCases) { std::string longKeyPath(1000, 'a'); EXPECT_EQ(registry.createKey(longKeyPath), RegistryResult::SUCCESS); EXPECT_TRUE(registry.keyExists(longKeyPath)); - + // 非常长的值名称 std::string longValueName(1000, 'b'); - EXPECT_EQ(registry.setValue(testKeyPath, longValueName, "TestData"), + EXPECT_EQ(registry.setValue(testKeyPath, longValueName, "TestData"), RegistryResult::SUCCESS); EXPECT_TRUE(registry.valueExists(testKeyPath, longValueName)); - + // 非常长的值数据 std::string longValueData(10000, 'c'); - EXPECT_EQ(registry.setValue(testKeyPath, "LongDataValue", longValueData), + EXPECT_EQ(registry.setValue(testKeyPath, "LongDataValue", longValueData), RegistryResult::SUCCESS); auto value = registry.getValue(testKeyPath, "LongDataValue"); EXPECT_TRUE(value.has_value()); EXPECT_EQ(value.value(), longValueData); - + // 空键路径 EXPECT_EQ(registry.createKey(""), RegistryResult::INVALID_FORMAT); - + // 空值名称 - EXPECT_EQ(registry.setValue(testKeyPath, "", "EmptyNameData"), + EXPECT_EQ(registry.setValue(testKeyPath, "", "EmptyNameData"), RegistryResult::INVALID_FORMAT); - + // 嵌套级别非常深的键 std::string deepKeyPath; for (int i = 0; i < 100; ++i) { deepKeyPath += "Level" + std::to_string(i) + "/"; } deepKeyPath += "FinalKey"; - + EXPECT_EQ(registry.createKey(deepKeyPath), RegistryResult::SUCCESS); EXPECT_TRUE(registry.keyExists(deepKeyPath)); } @@ -730,38 +730,38 @@ TEST_F(RegistryTest, EdgeCases) { TEST_F(RegistryTest, Encryption) { // 初始化一个新的带加密的注册表 Registry encryptedRegistry; - + auto tempDir = std::filesystem::temp_directory_path(); std::string encryptedFilePath = (tempDir / "encrypted_registry.dat").string(); - - EXPECT_EQ(encryptedRegistry.initialize(encryptedFilePath, true), + + EXPECT_EQ(encryptedRegistry.initialize(encryptedFilePath, true), RegistryResult::SUCCESS); - + // 设置一些数据 EXPECT_EQ(encryptedRegistry.createKey("EncryptedKey"), RegistryResult::SUCCESS); - EXPECT_EQ(encryptedRegistry.setValue("EncryptedKey", "SecretValue", "SecretData"), + EXPECT_EQ(encryptedRegistry.setValue("EncryptedKey", "SecretValue", "SecretData"), RegistryResult::SUCCESS); - + // 确保数据已正确存储 auto value = encryptedRegistry.getValue("EncryptedKey", "SecretValue"); EXPECT_TRUE(value.has_value()); EXPECT_EQ(value.value(), "SecretData"); - + // 检查文件是否存在 EXPECT_TRUE(std::filesystem::exists(encryptedFilePath)); - + // 尝试不用加密打开加密的文件(应该会失败或者读取数据错误) Registry nonEncryptedRegistry; - EXPECT_EQ(nonEncryptedRegistry.initialize(encryptedFilePath, false), + EXPECT_EQ(nonEncryptedRegistry.initialize(encryptedFilePath, false), RegistryResult::SUCCESS); - + // 尝试读取数据,这可能成功或失败,取决于实现 // 但即使成功,也不应该匹配原始数据 auto attemptedValue = nonEncryptedRegistry.getValue("EncryptedKey", "SecretValue"); if (attemptedValue.has_value()) { EXPECT_NE(attemptedValue.value(), "SecretData"); } - + // 清理 std::filesystem::remove(encryptedFilePath); -} \ No newline at end of file +} diff --git a/tests/system/test_network_manager.hpp b/tests/system/test_network_manager.hpp index fd09d6de..eab1d90c 100644 --- a/tests/system/test_network_manager.hpp +++ b/tests/system/test_network_manager.hpp @@ -23,7 +23,7 @@ class NetworkManagerTest : public ::testing::Test { // Get a list of network interfaces for testing interfaces = manager->getNetworkInterfaces(); - + // If we have at least one interface, save its name for tests if (!interfaces.empty()) { test_interface_name = interfaces[0].getName(); @@ -47,7 +47,7 @@ class NetworkManagerTest : public ::testing::Test { // Helper method: wait for a condition to be true template - bool wait_for_condition(Func condition, + bool wait_for_condition(Func condition, std::chrono::milliseconds timeout = 5s) { auto start = std::chrono::steady_clock::now(); while (!condition()) { @@ -85,7 +85,7 @@ TEST_F(NetworkManagerTest, NetworkInterfaceBasics) { ASSERT_FALSE(mutable_addresses.empty()); std::string original_address = mutable_addresses[0]; mutable_addresses[0] = "10.0.0.1"; - + EXPECT_EQ(interface.getAddresses()[0], "10.0.0.1"); EXPECT_NE(interface.getAddresses()[0], original_address); } @@ -100,7 +100,7 @@ TEST_F(NetworkManagerTest, ConstructorDefault) { // Test getting network interfaces TEST_F(NetworkManagerTest, GetNetworkInterfaces) { auto interfaces = manager->getNetworkInterfaces(); - + // We should get at least one interface on most systems EXPECT_FALSE(interfaces.empty()); @@ -108,7 +108,7 @@ TEST_F(NetworkManagerTest, GetNetworkInterfaces) { for (const auto& interface : interfaces) { EXPECT_FALSE(interface.getName().empty()); EXPECT_FALSE(interface.getMac().empty()); - + // An interface may not have addresses, but if it does they should be valid for (const auto& address : interface.getAddresses()) { EXPECT_FALSE(address.empty()); @@ -128,10 +128,10 @@ TEST_F(NetworkManagerTest, EnableDisableInterface) { // Just verify the methods don't throw exceptions EXPECT_NO_THROW(NetworkManager::enableInterface(test_interface_name)); std::this_thread::sleep_for(wait_time); - + EXPECT_NO_THROW(NetworkManager::disableInterface(test_interface_name)); std::this_thread::sleep_for(wait_time); - + // Re-enable for good measure EXPECT_NO_THROW(NetworkManager::enableInterface(test_interface_name)); } @@ -140,10 +140,10 @@ TEST_F(NetworkManagerTest, EnableDisableInterface) { TEST_F(NetworkManagerTest, ResolveDNS) { // Try to resolve a common hostname std::string ip = NetworkManager::resolveDNS(test_hostname); - + // Verify we got a non-empty result EXPECT_FALSE(ip.empty()); - + // Check that it looks like an IPv4 or IPv6 address bool valid_format = ip.find('.') != std::string::npos || ip.find(':') != std::string::npos; EXPECT_TRUE(valid_format); @@ -153,7 +153,7 @@ TEST_F(NetworkManagerTest, ResolveDNS) { TEST_F(NetworkManagerTest, MonitorConnectionStatus) { // Since this starts a background task, we just verify it doesn't throw EXPECT_NO_THROW(manager->monitorConnectionStatus()); - + // Give it some time to run std::this_thread::sleep_for(300ms); } @@ -164,10 +164,10 @@ TEST_F(NetworkManagerTest, GetInterfaceStatus) { if (interfaces.empty()) { GTEST_SKIP() << "No network interfaces found for testing"; } - + // Get status of an interface std::string status = manager->getInterfaceStatus(test_interface_name); - + // Status should not be empty EXPECT_FALSE(status.empty()); } @@ -176,25 +176,25 @@ TEST_F(NetworkManagerTest, GetInterfaceStatus) { TEST_F(NetworkManagerTest, DNSServerManagement) { // Get current DNS servers auto original_dns = NetworkManager::getDNSServers(); - + // Add a test DNS server std::string test_dns = "8.8.8.8"; NetworkManager::addDNSServer(test_dns); - + // Get updated DNS servers auto updated_dns = NetworkManager::getDNSServers(); - + // The list may have changed but we can't always verify the exact content // as it may require admin privileges to actually change DNS settings - + // Try to restore original settings NetworkManager::setDNSServers(original_dns); - + // Try to remove a DNS server if (!updated_dns.empty()) { NetworkManager::removeDNSServer(updated_dns[0]); } - + // These tests mainly check that the methods don't throw exceptions SUCCEED(); } @@ -203,20 +203,20 @@ TEST_F(NetworkManagerTest, DNSServerManagement) { TEST_F(NetworkManagerTest, GetMacAddress) { // This test accesses a private method, so we can't directly test it // We can indirectly test it through the NetworkInterface objects - + // Skip if no interfaces if (interfaces.empty()) { GTEST_SKIP() << "No network interfaces found for testing"; } - + // Check that all interfaces have a MAC address for (const auto& interface : interfaces) { EXPECT_FALSE(interface.getMac().empty()); - + // Verify MAC address format (XX:XX:XX:XX:XX:XX) std::string mac = interface.getMac(); EXPECT_EQ(17, mac.length()); // 6 pairs of 2 hex digits + 5 colons - + int colon_count = 0; for (char c : mac) { if (c == ':') colon_count++; @@ -231,7 +231,7 @@ TEST_F(NetworkManagerTest, IsInterfaceUp) { if (interfaces.empty()) { GTEST_SKIP() << "No network interfaces found for testing"; } - + // We know each interface has an isUp method, so we can test it for (const auto& interface : interfaces) { // Just verify that we can get a status - can't predict what it should be @@ -243,19 +243,19 @@ TEST_F(NetworkManagerTest, IsInterfaceUp) { // Test getting network connections for a process TEST_F(NetworkManagerTest, GetNetworkConnections) { // Use the current process ID or a known process - int pid = + int pid = #ifdef _WIN32 4; // System process on Windows often has network connections #else 1; // Init process on Unix-like systems #endif - + // Get connections for the process auto connections = getNetworkConnections(pid); - + // We can't predict if there will be connections, but we can verify the method runs SUCCEED(); - + // If there are connections, check they have valid data for (const auto& conn : connections) { EXPECT_FALSE(conn.protocol.empty()); @@ -269,11 +269,11 @@ TEST_F(NetworkManagerTest, GetNetworkConnections) { // Test with invalid interface name TEST_F(NetworkManagerTest, InvalidInterfaceName) { std::string invalid_name = "nonexistent_interface_xyz"; - + // Test interface status for non-existent interface std::string status = manager->getInterfaceStatus(invalid_name); EXPECT_FALSE(status.empty()); // Should return some kind of error status - + // Test enable/disable with invalid interface // Should not throw, but probably won't succeed EXPECT_NO_THROW(NetworkManager::enableInterface(invalid_name)); @@ -283,7 +283,7 @@ TEST_F(NetworkManagerTest, InvalidInterfaceName) { // Test DNS resolution with invalid hostname TEST_F(NetworkManagerTest, InvalidHostname) { std::string invalid_hostname = "thishostnamedoesnotexist.example.xyz"; - + // Resolving non-existent hostname should either return empty string, // an error message, or throw an exception that we can catch try { @@ -301,11 +301,11 @@ TEST_F(NetworkManagerTest, ConcurrentAccess) { if (interfaces.empty()) { GTEST_SKIP() << "No network interfaces found for testing"; } - + // Create multiple threads to access NetworkManager simultaneously const int num_threads = 5; std::vector> futures; - + for (int i = 0; i < num_threads; ++i) { futures.push_back(std::async(std::launch::async, [this, i]() { for (int j = 0; j < 10; ++j) { @@ -324,12 +324,12 @@ TEST_F(NetworkManagerTest, ConcurrentAccess) { } })); } - + // Wait for all threads to finish for (auto& future : futures) { future.wait(); } - + // If we got here without crashes or exceptions, the test passed SUCCEED(); } @@ -337,25 +337,25 @@ TEST_F(NetworkManagerTest, ConcurrentAccess) { // Test with network stress TEST_F(NetworkManagerTest, DISABLED_NetworkStress) { // This is a potentially intensive test, so it's disabled by default - + // Rapidly get network interfaces and other info const int iterations = 100; - + for (int i = 0; i < iterations; ++i) { auto interfaces = manager->getNetworkInterfaces(); for (const auto& interface : interfaces) { manager->getInterfaceStatus(interface.getName()); } - + auto dns_servers = NetworkManager::getDNSServers(); NetworkManager::resolveDNS(test_hostname); - + if (i % 10 == 0) { // Every 10 iterations, output progress std::cout << "Network stress test progress: " << i << "/" << iterations << std::endl; } } - + // If we got here without errors, the test passed SUCCEED(); } @@ -364,15 +364,15 @@ TEST_F(NetworkManagerTest, DISABLED_NetworkStress) { // This is difficult to fully automate as it requires changing network state TEST_F(NetworkManagerTest, DISABLED_NetworkStateChanges) { // This test is disabled as it would require manual intervention - + std::cout << "This test requires manually changing network state:" << std::endl; std::cout << "1. Run the test" << std::endl; std::cout << "2. Manually disable/enable network interfaces or connections" << std::endl; std::cout << "3. The test will check for appropriate state changes" << std::endl; - + // Start monitoring connection status manager->monitorConnectionStatus(); - + // Monitor for 30 seconds, periodically checking interface status const int check_intervals = 30; for (int i = 0; i < check_intervals; ++i) { @@ -381,10 +381,10 @@ TEST_F(NetworkManagerTest, DISABLED_NetworkStateChanges) { std::string status = manager->getInterfaceStatus(interface.getName()); std::cout << "Interface " << interface.getName() << " status: " << status << std::endl; } - + std::this_thread::sleep_for(1s); } - + // If we got here without crashes, the test passed SUCCEED(); } @@ -393,4 +393,4 @@ TEST_F(NetworkManagerTest, DISABLED_NetworkStateChanges) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/system/test_pidwatcher.hpp b/tests/system/test_pidwatcher.hpp index 455ab533..4e7df9cd 100644 --- a/tests/system/test_pidwatcher.hpp +++ b/tests/system/test_pidwatcher.hpp @@ -769,4 +769,4 @@ TEST_F(PidWatcherTest, DISABLED_LoadTest) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/system/test_stat.hpp b/tests/system/test_stat.hpp index fdc2546e..e5747065 100644 --- a/tests/system/test_stat.hpp +++ b/tests/system/test_stat.hpp @@ -491,4 +491,4 @@ TEST_F(StatTest, FormatTimeEdgeCases) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/system/test_voltage.cpp b/tests/system/test_voltage.cpp index 90a00af1..763ccb32 100644 --- a/tests/system/test_voltage.cpp +++ b/tests/system/test_voltage.cpp @@ -33,20 +33,20 @@ class VoltageMonitorTest : public ::testing::Test { void SetUp() override { // Create a real voltage monitor realMonitor = VoltageMonitor::create(); - + // Create a mock voltage monitor for controlled tests mockMonitor = std::make_unique<::testing::NiceMock>(); // Set up default behavior for the mock ON_CALL(*mockMonitor, getPlatformName()) .WillByDefault(::testing::Return("MockPlatform")); - + ON_CALL(*mockMonitor, getInputVoltage()) .WillByDefault(::testing::Return(std::optional(220.0))); - + ON_CALL(*mockMonitor, getBatteryVoltage()) .WillByDefault(::testing::Return(std::optional(12.0))); - + ON_CALL(*mockMonitor, getAllPowerSources()) .WillByDefault(::testing::Return(createSamplePowerSources())); } @@ -59,7 +59,7 @@ class VoltageMonitorTest : public ::testing::Test { // Helper method to create sample power sources for testing std::vector createSamplePowerSources() { std::vector sources; - + // Create an AC power source PowerSourceInfo acSource; acSource.name = "Test AC Adapter"; @@ -67,7 +67,7 @@ class VoltageMonitorTest : public ::testing::Test { acSource.voltage = 220.0; acSource.current = 1.5; sources.push_back(acSource); - + // Create a battery power source PowerSourceInfo batterySource; batterySource.name = "Test Battery"; @@ -77,7 +77,7 @@ class VoltageMonitorTest : public ::testing::Test { batterySource.chargePercent = 75; batterySource.isCharging = true; sources.push_back(batterySource); - + // Create a USB power source PowerSourceInfo usbSource; usbSource.name = "Test USB"; @@ -85,7 +85,7 @@ class VoltageMonitorTest : public ::testing::Test { usbSource.voltage = 5.0; usbSource.current = 0.5; sources.push_back(usbSource); - + return sources; } @@ -97,14 +97,14 @@ class VoltageMonitorTest : public ::testing::Test { TEST_F(VoltageMonitorTest, Create) { auto monitor = VoltageMonitor::create(); ASSERT_NE(monitor, nullptr); - + // Check that the platform name is not empty EXPECT_FALSE(monitor->getPlatformName().empty()); - + // Platform name should be Windows, Linux, or MacOS std::string platform = monitor->getPlatformName(); - bool validPlatform = (platform == "Windows" || - platform == "Linux" || + bool validPlatform = (platform == "Windows" || + platform == "Linux" || platform == "MacOS"); EXPECT_TRUE(validPlatform); } @@ -119,9 +119,9 @@ TEST_F(VoltageMonitorTest, PowerSourceInfoToString) { info.current = 1.2; info.chargePercent = 80; info.isCharging = true; - + std::string infoStr = info.toString(); - + // Verify all information is included in the string EXPECT_TRUE(infoStr.find("Test Source") != std::string::npos); EXPECT_TRUE(infoStr.find("Battery") != std::string::npos); @@ -129,13 +129,13 @@ TEST_F(VoltageMonitorTest, PowerSourceInfoToString) { EXPECT_TRUE(infoStr.find("1.20A") != std::string::npos); EXPECT_TRUE(infoStr.find("80%") != std::string::npos); EXPECT_TRUE(infoStr.find("Charging") != std::string::npos); - + // Now test with some fields missing PowerSourceInfo partialInfo; partialInfo.name = "Partial Info"; partialInfo.type = PowerSourceType::AC; // Missing voltage, current, etc. - + std::string partialStr = partialInfo.toString(); EXPECT_TRUE(partialStr.find("Partial Info") != std::string::npos); EXPECT_TRUE(partialStr.find("AC Power") != std::string::npos); @@ -148,7 +148,7 @@ TEST_F(VoltageMonitorTest, PowerSourceTypeToString) { EXPECT_EQ(powerSourceTypeToString(PowerSourceType::Battery), "Battery"); EXPECT_EQ(powerSourceTypeToString(PowerSourceType::USB), "USB"); EXPECT_EQ(powerSourceTypeToString(PowerSourceType::Unknown), "Unknown"); - + // Test with explicit cast to test default case EXPECT_EQ(powerSourceTypeToString(static_cast(999)), "Undefined"); } @@ -159,7 +159,7 @@ TEST_F(VoltageMonitorTest, GetInputVoltage) { auto voltage = mockMonitor->getInputVoltage(); ASSERT_TRUE(voltage.has_value()); EXPECT_EQ(*voltage, 220.0); - + // Test with the real monitor // Note: This might return nullopt if no AC power is connected auto realVoltage = realMonitor->getInputVoltage(); @@ -178,7 +178,7 @@ TEST_F(VoltageMonitorTest, GetBatteryVoltage) { auto voltage = mockMonitor->getBatteryVoltage(); ASSERT_TRUE(voltage.has_value()); EXPECT_EQ(*voltage, 12.0); - + // Test with the real monitor // Note: This might return nullopt if no battery is present auto realVoltage = realMonitor->getBatteryVoltage(); @@ -196,13 +196,13 @@ TEST_F(VoltageMonitorTest, GetAllPowerSources) { // Using the mock monitor for deterministic testing auto sources = mockMonitor->getAllPowerSources(); ASSERT_EQ(sources.size(), 3); - + // Check first source (AC) EXPECT_EQ(sources[0].name, "Test AC Adapter"); EXPECT_EQ(sources[0].type, PowerSourceType::AC); ASSERT_TRUE(sources[0].voltage.has_value()); EXPECT_EQ(*sources[0].voltage, 220.0); - + // Check second source (Battery) EXPECT_EQ(sources[1].name, "Test Battery"); EXPECT_EQ(sources[1].type, PowerSourceType::Battery); @@ -212,13 +212,13 @@ TEST_F(VoltageMonitorTest, GetAllPowerSources) { EXPECT_EQ(*sources[1].chargePercent, 75); ASSERT_TRUE(sources[1].isCharging.has_value()); EXPECT_TRUE(*sources[1].isCharging); - + // Check third source (USB) EXPECT_EQ(sources[2].name, "Test USB"); EXPECT_EQ(sources[2].type, PowerSourceType::USB); ASSERT_TRUE(sources[2].voltage.has_value()); EXPECT_EQ(*sources[2].voltage, 5.0); - + // Test with the real monitor auto realSources = realMonitor->getAllPowerSources(); // We don't know how many power sources are available on the test system @@ -237,11 +237,11 @@ TEST_F(VoltageMonitorTest, GetAllPowerSources) { TEST_F(VoltageMonitorTest, GetPlatformName) { // Using the mock monitor for deterministic testing EXPECT_EQ(mockMonitor->getPlatformName(), "MockPlatform"); - + // Test with the real monitor std::string platform = realMonitor->getPlatformName(); EXPECT_FALSE(platform.empty()); - + // Platform name should match the current platform #ifdef _WIN32 EXPECT_EQ(platform, "Windows"); @@ -257,7 +257,7 @@ TEST_F(VoltageMonitorTest, GetInputVoltageNullopt) { // Make the mock return nullopt EXPECT_CALL(*mockMonitor, getInputVoltage()) .WillOnce(::testing::Return(std::nullopt)); - + auto voltage = mockMonitor->getInputVoltage(); EXPECT_FALSE(voltage.has_value()); } @@ -267,7 +267,7 @@ TEST_F(VoltageMonitorTest, GetBatteryVoltageNullopt) { // Make the mock return nullopt EXPECT_CALL(*mockMonitor, getBatteryVoltage()) .WillOnce(::testing::Return(std::nullopt)); - + auto voltage = mockMonitor->getBatteryVoltage(); EXPECT_FALSE(voltage.has_value()); } @@ -277,7 +277,7 @@ TEST_F(VoltageMonitorTest, GetAllPowerSourcesEmpty) { // Make the mock return an empty list EXPECT_CALL(*mockMonitor, getAllPowerSources()) .WillOnce(::testing::Return(std::vector())); - + auto sources = mockMonitor->getAllPowerSources(); EXPECT_TRUE(sources.empty()); } @@ -289,10 +289,10 @@ TEST_F(VoltageMonitorTest, GetAllPowerSourcesEmpty) { TEST_F(VoltageMonitorTest, WindowsSpecificTests) { // Check that our real monitor is a WindowsVoltageMonitor EXPECT_EQ(typeid(*realMonitor).name(), typeid(WindowsVoltageMonitor).name()); - + // Test that platform name is correctly reported EXPECT_EQ(realMonitor->getPlatformName(), "Windows"); - + // Additional Windows-specific tests could go here } #elif defined(__linux__) @@ -300,18 +300,18 @@ TEST_F(VoltageMonitorTest, WindowsSpecificTests) { TEST_F(VoltageMonitorTest, LinuxSpecificTests) { // Check that our real monitor is a LinuxVoltageMonitor EXPECT_EQ(typeid(*realMonitor).name(), typeid(LinuxVoltageMonitor).name()); - + // Test that platform name is correctly reported EXPECT_EQ(realMonitor->getPlatformName(), "Linux"); - + // Test LinuxVoltageMonitor specific methods auto* linuxMonitor = dynamic_cast(realMonitor.get()); ASSERT_NE(linuxMonitor, nullptr); - + // Test conversion methods EXPECT_NEAR(LinuxVoltageMonitor::microvoltsToVolts("1000000"), 1.0, 0.001); EXPECT_NEAR(LinuxVoltageMonitor::microampsToAmps("1000000"), 1.0, 0.001); - + // Invalid input should return 0 EXPECT_EQ(LinuxVoltageMonitor::microvoltsToVolts("invalid"), 0.0); EXPECT_EQ(LinuxVoltageMonitor::microampsToAmps("invalid"), 0.0); @@ -326,7 +326,7 @@ TEST_F(VoltageMonitorTest, InvalidPowerSourceType) { info.name = "Invalid Type Test"; // Set an invalid type using a cast info.type = static_cast(999); - + std::string infoStr = info.toString(); EXPECT_TRUE(infoStr.find("Undefined") != std::string::npos); } @@ -336,14 +336,14 @@ TEST_F(VoltageMonitorTest, ExtremeValues) { PowerSourceInfo info; info.name = "Extreme Values Test"; info.type = PowerSourceType::Battery; - + // Very high voltage info.voltage = 1000000.0; // Very high current info.current = 1000000.0; // 100% charge info.chargePercent = 100; - + std::string infoStr = info.toString(); EXPECT_TRUE(infoStr.find("1000000.00V") != std::string::npos); EXPECT_TRUE(infoStr.find("1000000.00A") != std::string::npos); @@ -355,14 +355,14 @@ TEST_F(VoltageMonitorTest, NegativeValues) { PowerSourceInfo info; info.name = "Negative Values Test"; info.type = PowerSourceType::Battery; - + // Negative voltage (shouldn't happen in reality) info.voltage = -12.0; // Negative current (could indicate discharge) info.current = -1.5; // Negative charge percentage (shouldn't happen in reality) info.chargePercent = -10; - + std::string infoStr = info.toString(); EXPECT_TRUE(infoStr.find("-12.00V") != std::string::npos); EXPECT_TRUE(infoStr.find("-1.50A") != std::string::npos); @@ -373,22 +373,22 @@ TEST_F(VoltageMonitorTest, NegativeValues) { TEST_F(VoltageMonitorTest, IntegrationTest) { // Create a new monitor auto monitor = VoltageMonitor::create(); - + // Test platform name std::string platform = monitor->getPlatformName(); EXPECT_FALSE(platform.empty()); - + // Get input voltage auto inputVoltage = monitor->getInputVoltage(); // Don't assert on value, just that it works - + // Get battery voltage auto batteryVoltage = monitor->getBatteryVoltage(); // Don't assert on value, just that it works - + // Get all power sources auto sources = monitor->getAllPowerSources(); - + // Print all source information using toString for (const auto& source : sources) { std::string sourceInfo = source.toString(); @@ -400,20 +400,20 @@ TEST_F(VoltageMonitorTest, IntegrationTest) { TEST_F(VoltageMonitorTest, DISABLED_PerformanceTest) { // Time how long it takes to get power source information const int iterations = 100; - + auto start = std::chrono::high_resolution_clock::now(); - + for (int i = 0; i < iterations; ++i) { auto sources = realMonitor->getAllPowerSources(); } - + auto end = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast(end - start).count(); - - std::cout << "Average time to get all power sources: " - << (duration / static_cast(iterations)) + + std::cout << "Average time to get all power sources: " + << (duration / static_cast(iterations)) << " ms" << std::endl; - + // No specific assertion, but it shouldn't take too long } @@ -421,4 +421,4 @@ TEST_F(VoltageMonitorTest, DISABLED_PerformanceTest) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/system/test_wregistry.cpp b/tests/system/test_wregistry.cpp index 74b57bf5..90f5bc40 100644 --- a/tests/system/test_wregistry.cpp +++ b/tests/system/test_wregistry.cpp @@ -448,4 +448,4 @@ TEST(WRegistryTest, NonWindowsPlatform) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/type/test_auto_table.cpp b/tests/type/test_auto_table.cpp index 102fa856..420e9e55 100644 --- a/tests/type/test_auto_table.cpp +++ b/tests/type/test_auto_table.cpp @@ -563,4 +563,4 @@ TEST_F(CountingHashTableTest, DISABLED_PerformanceTest) { SUCCEED() << "Performance test completed"; } -#endif // ATOM_TYPE_TEST_AUTO_TABLE_HPP \ No newline at end of file +#endif // ATOM_TYPE_TEST_AUTO_TABLE_HPP diff --git a/tests/type/test_concurrent_set.hpp b/tests/type/test_concurrent_set.hpp index 6c659f71..643845f8 100644 --- a/tests/type/test_concurrent_set.hpp +++ b/tests/type/test_concurrent_set.hpp @@ -868,4 +868,4 @@ TEST_F(ConcurrentSetTest, FileOperationEdgeCases) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/type/test_expected.cpp b/tests/type/test_expected.cpp index 04363e29..e1a6255a 100644 --- a/tests/type/test_expected.cpp +++ b/tests/type/test_expected.cpp @@ -506,4 +506,4 @@ TEST_F(ExpectedTest, CustomTypes) { EXPECT_EQ(result2.error().error(), "Person is underage"); } -} // namespace \ No newline at end of file +} // namespace diff --git a/tests/type/test_indestructible.hpp b/tests/type/test_indestructible.hpp index d74a3dc7..174e92ff 100644 --- a/tests/type/test_indestructible.hpp +++ b/tests/type/test_indestructible.hpp @@ -415,4 +415,4 @@ TEST_F(IndestructibleTest, DirectStructInit) { EXPECT_EQ(point->x, 30); EXPECT_EQ(point->y, 40); -} \ No newline at end of file +} diff --git a/tests/type/test_iter.hpp b/tests/type/test_iter.hpp index 0f6cc338..55c24086 100644 --- a/tests/type/test_iter.hpp +++ b/tests/type/test_iter.hpp @@ -588,4 +588,4 @@ TEST_F(IteratorTest, DISABLED_LargeContainer) { << " elements" << std::endl; EXPECT_EQ(count, SIZE / 2); -} \ No newline at end of file +} diff --git a/tests/type/test_json-schema.hpp b/tests/type/test_json-schema.hpp index 45a195c6..54ee2b90 100644 --- a/tests/type/test_json-schema.hpp +++ b/tests/type/test_json-schema.hpp @@ -486,4 +486,4 @@ TEST_F(JsonValidatorTest, SchemaDependency) { ASSERT_GE(errors.size(), 1); EXPECT_THAT(errors[0].message, HasSubstr("Missing required field")); EXPECT_THAT(errors[0].message, HasSubstr("security_code")); -} \ No newline at end of file +} diff --git a/tests/type/test_no_offset_ptr.hpp b/tests/type/test_no_offset_ptr.hpp index 38113f97..e172e843 100644 --- a/tests/type/test_no_offset_ptr.hpp +++ b/tests/type/test_no_offset_ptr.hpp @@ -27,13 +27,13 @@ class SimpleTestClass { SimpleTestClass(const SimpleTestClass& other) : value(other.value) { instances++; } - + // 修复未使用的参数警告 SimpleTestClass(SimpleTestClass&& other) noexcept : value(other.value) { other.value = 0; instances++; } - + ~SimpleTestClass() { instances--; } private: @@ -394,4 +394,4 @@ TEST(NoOffsetPtrPolicyTest, AtomicPolicy) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/type/test_optional.hpp b/tests/type/test_optional.hpp index 76b612b8..33151d18 100644 --- a/tests/type/test_optional.hpp +++ b/tests/type/test_optional.hpp @@ -541,4 +541,4 @@ TEST(OptionalPerformanceTest, CompareWithStdOptional) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/type/test_pod_vector.cpp b/tests/type/test_pod_vector.cpp index 8a57b4f5..c67d6140 100644 --- a/tests/type/test_pod_vector.cpp +++ b/tests/type/test_pod_vector.cpp @@ -517,4 +517,4 @@ TEST(PodVectorBoostTest, BoostFunctionality) { } // namespace atom::type::test -#endif // ATOM_TYPE_TEST_POD_VECTOR_HPP \ No newline at end of file +#endif // ATOM_TYPE_TEST_POD_VECTOR_HPP diff --git a/tests/type/test_pointer.hpp b/tests/type/test_pointer.hpp index fb55be50..d187a0a0 100644 --- a/tests/type/test_pointer.hpp +++ b/tests/type/test_pointer.hpp @@ -590,4 +590,4 @@ TEST_F(PointerSentinelTest, VoidReturnTypes) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/type/test_rjson.cpp b/tests/type/test_rjson.cpp index 9f6dca6b..65f8604d 100644 --- a/tests/type/test_rjson.cpp +++ b/tests/type/test_rjson.cpp @@ -534,4 +534,4 @@ TEST_F(JsonParserTest, RoundtripComplexStructure) { // Check nested object EXPECT_EQ(value.asObject().at("object").asObject().at("key").asString(), reparsed.asObject().at("object").asObject().at("key").asString()); -} \ No newline at end of file +} diff --git a/tests/type/test_robin_hood.hpp b/tests/type/test_robin_hood.hpp index 4fe8a699..19b23fd4 100644 --- a/tests/type/test_robin_hood.hpp +++ b/tests/type/test_robin_hood.hpp @@ -41,17 +41,17 @@ TEST_F(RobinHoodMapTest, Construction) { unordered_flat_map map1; EXPECT_TRUE(map1.empty()); EXPECT_EQ(map1.size(), 0); - + // Constructor with threading policy unordered_flat_map map2( unordered_flat_map::threading_policy::mutex); EXPECT_TRUE(map2.empty()); - + // Constructor with allocator std::allocator> alloc; unordered_flat_map map3(alloc); EXPECT_TRUE(map3.empty()); - + // Constructor with bucket count and allocator unordered_flat_map map4(16, alloc); EXPECT_TRUE(map4.empty()); @@ -61,18 +61,18 @@ TEST_F(RobinHoodMapTest, Construction) { // Test basic capacity and size operations TEST_F(RobinHoodMapTest, CapacityAndSize) { unordered_flat_map map; - + EXPECT_TRUE(map.empty()); EXPECT_EQ(map.size(), 0); - + // Insert some elements map.insert(1, "one"); EXPECT_FALSE(map.empty()); EXPECT_EQ(map.size(), 1); - + map.insert(2, "two"); EXPECT_EQ(map.size(), 2); - + // Clear the map map.clear(); EXPECT_TRUE(map.empty()); @@ -82,23 +82,23 @@ TEST_F(RobinHoodMapTest, CapacityAndSize) { // Test basic insertion and lookup TEST_F(RobinHoodMapTest, InsertionAndLookup) { unordered_flat_map map; - + // Insert and verify auto [it1, inserted1] = map.insert(1, "one"); EXPECT_TRUE(inserted1); EXPECT_EQ(it1->first, 1); EXPECT_EQ(it1->second, "one"); - + // Lookup with at() EXPECT_EQ(map.at(1), "one"); - + // Check exception for non-existent key EXPECT_THROW(map.at(99), std::out_of_range); - + // Insert multiple elements map.insert(2, "two"); map.insert(3, "three"); - + EXPECT_EQ(map.size(), 3); EXPECT_EQ(map.at(2), "two"); EXPECT_EQ(map.at(3), "three"); @@ -108,7 +108,7 @@ TEST_F(RobinHoodMapTest, InsertionAndLookup) { TEST_F(RobinHoodMapTest, Iterators) { unordered_flat_map map; fill_test_map(map, 10); - + // Count elements using iterators size_t count = 0; for (auto it = map.begin(); it != map.end(); ++it) { @@ -117,7 +117,7 @@ TEST_F(RobinHoodMapTest, Iterators) { EXPECT_TRUE(std::find(test_keys.begin(), test_keys.end(), it->first) != test_keys.end()); } EXPECT_EQ(count, 10); - + // Test const iterators const auto& const_map = map; count = 0; @@ -125,7 +125,7 @@ TEST_F(RobinHoodMapTest, Iterators) { ++count; } EXPECT_EQ(count, 10); - + // Test cbegin/cend count = 0; for (auto it = map.cbegin(); it != map.cend(); ++it) { @@ -137,23 +137,23 @@ TEST_F(RobinHoodMapTest, Iterators) { // Test rehashing and load factor TEST_F(RobinHoodMapTest, RehashingAndLoadFactor) { unordered_flat_map map; - + // Default load factor should be 0.9 EXPECT_FLOAT_EQ(map.max_load_factor(), 0.9f); - + // Change load factor map.max_load_factor(0.75f); EXPECT_FLOAT_EQ(map.max_load_factor(), 0.75f); - + // Insert elements until rehashing occurs size_t initial_bucket_count = map.bucket_count(); if (initial_bucket_count > 0) { size_t elements_to_add = static_cast(initial_bucket_count * map.max_load_factor()) + 1; - + for (size_t i = 0; i < elements_to_add; ++i) { map.insert(static_cast(i), "value-" + std::to_string(i)); } - + // Verify that rehashing occurred EXPECT_GT(map.bucket_count(), initial_bucket_count); } @@ -162,15 +162,15 @@ TEST_F(RobinHoodMapTest, RehashingAndLoadFactor) { // Test with a large number of elements TEST_F(RobinHoodMapTest, LargeNumberOfElements) { unordered_flat_map map; - + // Insert a large number of elements const size_t num_elements = 1000; for (size_t i = 0; i < num_elements; ++i) { map.insert(static_cast(i), "value-" + std::to_string(i)); } - + EXPECT_EQ(map.size(), num_elements); - + // Verify all elements can be found for (size_t i = 0; i < num_elements; ++i) { EXPECT_EQ(map.at(static_cast(i)), "value-" + std::to_string(i)); @@ -181,14 +181,14 @@ TEST_F(RobinHoodMapTest, LargeNumberOfElements) { TEST_F(RobinHoodMapTest, ThreadSafetyWithReaderLocks) { unordered_flat_map map( unordered_flat_map::threading_policy::reader_lock); - + // Fill the map with some test data fill_test_map(map, 100); - + // Create multiple reader threads std::vector threads; std::vector results(10, false); - + for (size_t i = 0; i < 10; ++i) { threads.emplace_back([&map, i, &results]() { try { @@ -208,12 +208,12 @@ TEST_F(RobinHoodMapTest, ThreadSafetyWithReaderLocks) { } }); } - + // Wait for all threads to complete for (auto& thread : threads) { thread.join(); } - + // Verify all threads succeeded for (bool result : results) { EXPECT_TRUE(result); @@ -224,12 +224,12 @@ TEST_F(RobinHoodMapTest, ThreadSafetyWithReaderLocks) { TEST_F(RobinHoodMapTest, ThreadSafetyWithMutex) { unordered_flat_map map( unordered_flat_map::threading_policy::mutex); - + // Multiple threads insert different elements std::vector threads; const int num_threads = 10; const int elements_per_thread = 100; - + for (int i = 0; i < num_threads; ++i) { threads.emplace_back([&map, i, elements_per_thread]() { for (int j = 0; j < elements_per_thread; ++j) { @@ -238,15 +238,15 @@ TEST_F(RobinHoodMapTest, ThreadSafetyWithMutex) { } }); } - + // Wait for all threads to complete for (auto& thread : threads) { thread.join(); } - + // Verify size and all elements EXPECT_EQ(map.size(), static_cast(num_threads * elements_per_thread)); - + for (int i = 0; i < num_threads; ++i) { for (int j = 0; j < elements_per_thread; ++j) { int key = i * elements_per_thread + j; @@ -259,12 +259,12 @@ TEST_F(RobinHoodMapTest, ThreadSafetyWithMutex) { TEST_F(RobinHoodMapTest, ConcurrentReadsAndWrites) { unordered_flat_map map( unordered_flat_map::threading_policy::reader_lock); - + // Fill the map with initial data for (int i = 0; i < 100; ++i) { map.insert(i, "initial-" + std::to_string(i)); } - + // Create reader threads std::vector> reader_results; for (int i = 0; i < 5; ++i) { @@ -274,7 +274,7 @@ TEST_F(RobinHoodMapTest, ConcurrentReadsAndWrites) { for (int j = 0; j < 100; ++j) { try { std::string value = map.at(j); - if (value.find("initial-") == std::string::npos && + if (value.find("initial-") == std::string::npos && value.find("updated-") == std::string::npos) { return false; } @@ -287,7 +287,7 @@ TEST_F(RobinHoodMapTest, ConcurrentReadsAndWrites) { return true; })); } - + // Create writer threads std::vector> writer_results; for (int i = 0; i < 3; ++i) { @@ -304,12 +304,12 @@ TEST_F(RobinHoodMapTest, ConcurrentReadsAndWrites) { return true; })); } - + // Check results from all threads for (auto& result : reader_results) { EXPECT_TRUE(result.get()); } - + for (auto& result : writer_results) { EXPECT_TRUE(result.get()); } @@ -339,17 +339,17 @@ class CustomKeyEqual { TEST_F(RobinHoodMapTest, CustomHashAndKeyEqual) { unordered_flat_map map; - + // Insert with lowercase keys map.insert("one", 1); map.insert("two", 2); map.insert("three", 3); - + // Lookup with mixed case should work with our custom comparator EXPECT_EQ(map.at("ONE"), 1); EXPECT_EQ(map.at("Two"), 2); EXPECT_EQ(map.at("tHrEe"), 3); - + // Size should still be accurate EXPECT_EQ(map.size(), 3); } @@ -358,14 +358,14 @@ TEST_F(RobinHoodMapTest, CustomHashAndKeyEqual) { class MoveOnlyValue { public: explicit MoveOnlyValue(int val) : value(val) {} - + MoveOnlyValue(const MoveOnlyValue&) = delete; MoveOnlyValue& operator=(const MoveOnlyValue&) = delete; - + MoveOnlyValue(MoveOnlyValue&& other) noexcept : value(other.value) { other.value = -1; } - + MoveOnlyValue& operator=(MoveOnlyValue&& other) noexcept { if (this != &other) { value = other.value; @@ -373,20 +373,20 @@ class MoveOnlyValue { } return *this; } - + int get_value() const { return value; } - + private: int value; }; TEST_F(RobinHoodMapTest, MoveOnlyTypes) { unordered_flat_map map; - + // Insert with rvalue map.insert(1, MoveOnlyValue(100)); map.insert(2, MoveOnlyValue(200)); - + // Check values are correctly moved EXPECT_EQ(map.at(1).get_value(), 100); EXPECT_EQ(map.at(2).get_value(), 200); @@ -395,14 +395,14 @@ TEST_F(RobinHoodMapTest, MoveOnlyTypes) { // Test exception safety TEST_F(RobinHoodMapTest, ExceptionSafety) { unordered_flat_map map; - + // Insert some elements fill_test_map(map, 10); - + // Test exceptions from at() method EXPECT_THROW(map.at(999), std::out_of_range); EXPECT_EQ(map.size(), 10); // Size should be unchanged after exception - + // The const version const auto& const_map = map; EXPECT_THROW(const_map.at(999), std::out_of_range); @@ -412,4 +412,4 @@ TEST_F(RobinHoodMapTest, ExceptionSafety) { int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/type/test_rtype.hpp b/tests/type/test_rtype.hpp index a3754f99..c530a85f 100644 --- a/tests/type/test_rtype.hpp +++ b/tests/type/test_rtype.hpp @@ -511,4 +511,4 @@ TEST_F(RTypeTest, RoundtripYamlSerialization) { NestedType deserializedObj = nestedTypeReflection.from_yaml(yaml); EXPECT_EQ(deserializedObj, originalObj); -} \ No newline at end of file +} diff --git a/tests/type/test_ryaml.cpp b/tests/type/test_ryaml.cpp index bcb11e02..ef772ee4 100644 --- a/tests/type/test_ryaml.cpp +++ b/tests/type/test_ryaml.cpp @@ -691,4 +691,4 @@ not_a_number: .nan } // namespace atom::type::test -#endif // ATOM_TYPE_TEST_RYAML_HPP \ No newline at end of file +#endif // ATOM_TYPE_TEST_RYAML_HPP diff --git a/tests/type/test_small_list.hpp b/tests/type/test_small_list.hpp index 0e7dc222..34853257 100644 --- a/tests/type/test_small_list.hpp +++ b/tests/type/test_small_list.hpp @@ -32,7 +32,7 @@ TEST_F(SmallListTest, CopyConstructor) { list.pushBack(1); list.pushBack(2); list.pushBack(3); - + SmallList copy(list); EXPECT_EQ(copy.size(), list.size()); EXPECT_TRUE(std::equal(copy.begin(), copy.end(), list.begin())); @@ -42,7 +42,7 @@ TEST_F(SmallListTest, MoveConstructor) { list.pushBack(1); list.pushBack(2); size_t originalSize = list.size(); - + SmallList moved(std::move(list)); EXPECT_EQ(moved.size(), originalSize); EXPECT_TRUE(list.empty()); @@ -59,10 +59,10 @@ TEST_F(SmallListTest, PushBackAndFront) { TEST_F(SmallListTest, PopBackAndFront) { list.pushBack(1); list.pushBack(2); - + list.popFront(); EXPECT_EQ(list.front(), 2); - + list.popBack(); EXPECT_TRUE(list.empty()); } @@ -73,7 +73,7 @@ TEST_F(SmallListTest, EmplaceOperations) { auto it = list.begin(); ++it; list.emplace(it, 3); - + std::vector expected = {2, 3, 1}; EXPECT_TRUE(std::equal(list.begin(), list.end(), expected.begin())); } @@ -83,7 +83,7 @@ TEST_F(SmallListTest, IteratorOperations) { for(int i = 0; i < 5; ++i) { list.pushBack(i); } - + auto it = list.begin(); EXPECT_EQ(*it, 0); ++it; @@ -95,7 +95,7 @@ TEST_F(SmallListTest, IteratorOperations) { TEST_F(SmallListTest, ConstIterator) { list.pushBack(1); list.pushBack(2); - + const SmallList& constList = list; auto it = constList.begin(); EXPECT_EQ(*it, 1); @@ -118,7 +118,7 @@ TEST_F(SmallListTest, InsertAndErase) { ++it; list.insert(it, 3); EXPECT_THAT(list, ElementsAre(1, 2, 3, 4, 5)); - + it = list.begin(); ++it; list.erase(it); @@ -137,7 +137,7 @@ TEST_F(SmallListTest, Resize) { list.resize(5, 0); EXPECT_EQ(list.size(), 5); EXPECT_EQ(list.back(), 0); - + list.resize(2); EXPECT_EQ(list.size(), 2); EXPECT_EQ(list.back(), 2); @@ -203,7 +203,7 @@ TEST_F(SmallListTest, LargeListOperations) { for(int i = 0; i < TEST_SIZE; ++i) { list.pushBack(i); } - + list.sort(); EXPECT_TRUE(std::is_sorted(list.begin(), list.end())); EXPECT_EQ(list.size(), TEST_SIZE); @@ -213,7 +213,7 @@ TEST_F(SmallListTest, LargeListOperations) { struct ThrowingCopy { int value; static bool shouldThrow; - + ThrowingCopy(int v) : value(v) {} ThrowingCopy(const ThrowingCopy& other) { if(shouldThrow) throw std::runtime_error("Copy error"); @@ -228,7 +228,7 @@ TEST_F(SmallListTest, ExceptionSafety) { ThrowingCopy::shouldThrow = false; SmallList throwingList; throwingList.pushBack(ThrowingCopy(1)); - + ThrowingCopy::shouldThrow = true; EXPECT_THROW(throwingList.pushBack(ThrowingCopy(2)), std::runtime_error); EXPECT_EQ(throwingList.size(), 1); // List should remain unchanged @@ -237,4 +237,4 @@ TEST_F(SmallListTest, ExceptionSafety) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/type/test_small_vector.hpp b/tests/type/test_small_vector.hpp index d0f8e9d1..214a80db 100644 --- a/tests/type/test_small_vector.hpp +++ b/tests/type/test_small_vector.hpp @@ -1555,4 +1555,4 @@ TEST_F(SmallVectorTest, PopBack) { EXPECT_EQ(TestObject::destructor_count(), 1); EXPECT_EQ(sv.size(), 1u); } -} \ No newline at end of file +} diff --git a/tests/type/test_static_string.hpp b/tests/type/test_static_string.hpp index f731135b..4d7010ad 100644 --- a/tests/type/test_static_string.hpp +++ b/tests/type/test_static_string.hpp @@ -656,8 +656,8 @@ TEST_F(StaticStringTest, ConstexprUsage) { // Verify the constexpr values at runtime verifyStringEquals(constexpr_str, "Hello"); verifyStringEquals(str, "Hello"); - + // Additional runtime checks can go here EXPECT_EQ(constexpr_str.size(), 5); EXPECT_EQ(str.size(), 5); -} \ No newline at end of file +} diff --git a/tests/type/test_static_vector.hpp b/tests/type/test_static_vector.hpp index 921c31fa..224f53c8 100644 --- a/tests/type/test_static_vector.hpp +++ b/tests/type/test_static_vector.hpp @@ -938,4 +938,4 @@ TEST_F(StaticVectorTest, ThreadSafety) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/type/test_string.cpp b/tests/type/test_string.cpp index 1ee4b356..086c617d 100644 --- a/tests/type/test_string.cpp +++ b/tests/type/test_string.cpp @@ -508,4 +508,4 @@ TEST_F(StringTest, StreamOperations) { String s2("original"); badStream >> s2; EXPECT_EQ(s2.data(), "original"); // Should remain unchanged -} \ No newline at end of file +} diff --git a/tests/type/test_trackable.cpp b/tests/type/test_trackable.cpp index 9f60c859..e8597fe7 100644 --- a/tests/type/test_trackable.cpp +++ b/tests/type/test_trackable.cpp @@ -261,4 +261,4 @@ TEST_F(TrackableTest, ComplexTypeTracking) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/type/test_uint.cpp b/tests/type/test_uint.cpp index e48232a0..d5a36ffc 100644 --- a/tests/type/test_uint.cpp +++ b/tests/type/test_uint.cpp @@ -269,4 +269,4 @@ TEST_F(UintLiteralsTest, MaxConstants) { "MAX_UINT32 should be uint32_t"); } -} // namespace \ No newline at end of file +} // namespace diff --git a/tests/type/test_weak_ptr.hpp b/tests/type/test_weak_ptr.hpp index 6ca456a7..2add0e06 100644 --- a/tests/type/test_weak_ptr.hpp +++ b/tests/type/test_weak_ptr.hpp @@ -874,4 +874,4 @@ TEST_F(EnhancedWeakPtrTest, EdgeCases) { // Break cycle and test node.reset(); EXPECT_TRUE(self->weakSelf.expired()); -} \ No newline at end of file +} diff --git a/tests/utils/test_aes.hpp b/tests/utils/test_aes.hpp index 9abdb404..00504397 100644 --- a/tests/utils/test_aes.hpp +++ b/tests/utils/test_aes.hpp @@ -165,4 +165,4 @@ TEST_F(AESTest, CompressionRatio) { } // namespace atom::utils::test -#endif // ATOM_UTILS_TEST_AES_HPP \ No newline at end of file +#endif // ATOM_UTILS_TEST_AES_HPP diff --git a/tests/utils/test_aligned.hpp b/tests/utils/test_aligned.hpp index a64c45fc..621ac52d 100644 --- a/tests/utils/test_aligned.hpp +++ b/tests/utils/test_aligned.hpp @@ -147,4 +147,4 @@ struct CompilationFailureTests { } // namespace atom::utils::test -#endif // ATOM_UTILS_TEST_ALIGNED_HPP \ No newline at end of file +#endif // ATOM_UTILS_TEST_ALIGNED_HPP diff --git a/tests/utils/test_anyutils.hpp b/tests/utils/test_anyutils.hpp index 690bd0cb..1a6d83fe 100644 --- a/tests/utils/test_anyutils.hpp +++ b/tests/utils/test_anyutils.hpp @@ -489,4 +489,4 @@ TEST_F(CacheTest, CacheHitTest) { } // namespace atom::utils::test -#endif // ATOM_UTILS_TEST_ANYUTILS_HPP \ No newline at end of file +#endif // ATOM_UTILS_TEST_ANYUTILS_HPP diff --git a/tests/utils/test_bit.hpp b/tests/utils/test_bit.hpp index 67418558..545bcdbf 100644 --- a/tests/utils/test_bit.hpp +++ b/tests/utils/test_bit.hpp @@ -425,4 +425,4 @@ TEST_F(BitManipulationTest, ErrorHandling) { } // namespace atom::utils::test -#endif // ATOM_UTILS_TEST_BIT_HPP \ No newline at end of file +#endif // ATOM_UTILS_TEST_BIT_HPP diff --git a/tests/utils/test_container.hpp b/tests/utils/test_container.hpp index aa8d82ca..93ef78e9 100644 --- a/tests/utils/test_container.hpp +++ b/tests/utils/test_container.hpp @@ -464,4 +464,4 @@ TEST_F(ContainerTest, CombinedOperations) { } // namespace atom::utils::test -#endif // ATOM_UTILS_TEST_CONTAINER_HPP \ No newline at end of file +#endif // ATOM_UTILS_TEST_CONTAINER_HPP diff --git a/tests/utils/test_cstring.hpp b/tests/utils/test_cstring.hpp index cd3b8cfa..c95f91d0 100644 --- a/tests/utils/test_cstring.hpp +++ b/tests/utils/test_cstring.hpp @@ -36,19 +36,19 @@ TEST_F(CStringTest, Deduplicate) { // Basic deduplication auto result1 = deduplicate("hello"); EXPECT_EQ(arrayToString(result1), "helo"); - + // Empty string auto result2 = deduplicate(""); EXPECT_EQ(arrayToString(result2), ""); - + // String with no duplicates auto result3 = deduplicate("abcdef"); EXPECT_EQ(arrayToString(result3), "abcdef"); - + // String with all identical characters auto result4 = deduplicate("aaaaa"); EXPECT_EQ(arrayToString(result4), "a"); - + // String with special characters auto result5 = deduplicate("a!b!c!a!b!c!"); EXPECT_EQ(arrayToString(result5), "a!bc"); @@ -61,21 +61,21 @@ TEST_F(CStringTest, Split) { ASSERT_EQ(result1[0], "apple"); ASSERT_EQ(result1[1], "banana"); ASSERT_EQ(result1[2], "cherry"); - + // Split with empty parts auto result2 = split("apple,,cherry", ','); ASSERT_EQ(result2[0], "apple"); ASSERT_EQ(result2[1], ""); ASSERT_EQ(result2[2], "cherry"); - + // Split with no delimiter auto result3 = split("apple", ','); ASSERT_EQ(result3[0], "apple"); - + // Split empty string auto result4 = split("", ','); ASSERT_EQ(result4[0], ""); - + // Split with delimiter at start and end auto result5 = split(",apple,", ','); ASSERT_EQ(result5[0], ""); @@ -88,15 +88,15 @@ TEST_F(CStringTest, Replace) { // Basic replacement auto result1 = replace("hello", 'l', 'x'); EXPECT_EQ(arrayToString(result1), "hexxo"); - + // Replace character not in string auto result2 = replace("hello", 'z', 'x'); EXPECT_EQ(arrayToString(result2), "hello"); - + // Replace in empty string auto result3 = replace("", 'a', 'b'); EXPECT_EQ(arrayToString(result3), ""); - + // Replace with the same character auto result4 = replace("hello", 'l', 'l'); EXPECT_EQ(arrayToString(result4), "hello"); @@ -107,19 +107,19 @@ TEST_F(CStringTest, ToLower) { // Basic lowercase conversion auto result1 = toLower("HELLO"); EXPECT_EQ(arrayToString(result1), "hello"); - + // Mixed case auto result2 = toLower("HeLlO"); EXPECT_EQ(arrayToString(result2), "hello"); - + // Already lowercase auto result3 = toLower("hello"); EXPECT_EQ(arrayToString(result3), "hello"); - + // Empty string auto result4 = toLower(""); EXPECT_EQ(arrayToString(result4), ""); - + // Non-alphabetic characters auto result5 = toLower("Hello123!@#"); EXPECT_EQ(arrayToString(result5), "hello123!@#"); @@ -130,19 +130,19 @@ TEST_F(CStringTest, ToUpper) { // Basic uppercase conversion auto result1 = toUpper("hello"); EXPECT_EQ(arrayToString(result1), "HELLO"); - + // Mixed case auto result2 = toUpper("HeLlO"); EXPECT_EQ(arrayToString(result2), "HELLO"); - + // Already uppercase auto result3 = toUpper("HELLO"); EXPECT_EQ(arrayToString(result3), "HELLO"); - + // Empty string auto result4 = toUpper(""); EXPECT_EQ(arrayToString(result4), ""); - + // Non-alphabetic characters auto result5 = toUpper("Hello123!@#"); EXPECT_EQ(arrayToString(result5), "HELLO123!@#"); @@ -153,18 +153,18 @@ TEST_F(CStringTest, Concat) { // Basic concatenation auto result1 = concat("Hello, ", "World!"); EXPECT_EQ(arrayToString(result1), "Hello, World!"); - + // Concatenate with empty string auto result2 = concat("Hello", ""); EXPECT_EQ(arrayToString(result2), "Hello"); - + auto result3 = concat("", "World"); EXPECT_EQ(arrayToString(result3), "World"); - + // Concatenate two empty strings auto result4 = concat("", ""); EXPECT_EQ(arrayToString(result4), ""); - + // Concatenate with special characters auto result5 = concat("Hello\n", "World\t!"); EXPECT_EQ(arrayToString(result5), "Hello\nWorld\t!"); @@ -175,23 +175,23 @@ TEST_F(CStringTest, TrimCString) { // Basic trimming auto result1 = trim(" Hello "); EXPECT_EQ(arrayToString(result1), "Hello"); - + // No spaces to trim auto result2 = trim("Hello"); EXPECT_EQ(arrayToString(result2), "Hello"); - + // Only leading spaces auto result3 = trim(" Hello"); EXPECT_EQ(arrayToString(result3), "Hello"); - + // Only trailing spaces auto result4 = trim("Hello "); EXPECT_EQ(arrayToString(result4), "Hello"); - + // Only spaces auto result5 = trim(" "); EXPECT_EQ(arrayToString(result5), ""); - + // Empty string auto result6 = trim(""); EXPECT_EQ(arrayToString(result6), ""); @@ -202,19 +202,19 @@ TEST_F(CStringTest, Substring) { // Basic substring auto result1 = substring("Hello, World!", 7, 5); EXPECT_EQ(arrayToString(result1), "World"); - + // Substring from start auto result2 = substring("Hello, World!", 0, 5); EXPECT_EQ(arrayToString(result2), "Hello"); - + // Substring beyond string length auto result3 = substring("Hello", 0, 10); EXPECT_EQ(arrayToString(result3), "Hello"); - + // Empty substring auto result4 = substring("Hello", 0, 0); EXPECT_EQ(arrayToString(result4), ""); - + // Start beyond string length auto result5 = substring("Hello", 10, 5); EXPECT_EQ(arrayToString(result5), ""); @@ -224,19 +224,19 @@ TEST_F(CStringTest, Substring) { TEST_F(CStringTest, Equal) { // Equal strings EXPECT_TRUE(equal("Hello", "Hello")); - + // Different strings EXPECT_FALSE(equal("Hello", "World")); - + // Case sensitivity EXPECT_FALSE(equal("hello", "Hello")); - + // Different lengths EXPECT_FALSE(equal("Hello", "HelloWorld")); - + // Empty strings EXPECT_TRUE(equal("", "")); - + // One empty string EXPECT_FALSE(equal("Hello", "")); EXPECT_FALSE(equal("", "Hello")); @@ -246,19 +246,19 @@ TEST_F(CStringTest, Equal) { TEST_F(CStringTest, Find) { // Find existing character EXPECT_EQ(find("Hello", 'e'), 1); - + // Find first occurrence of repeated character EXPECT_EQ(find("Hello", 'l'), 2); - + // Character not found EXPECT_EQ(find("Hello", 'z'), 5); // Returns N-1 when not found - + // Empty string EXPECT_EQ(find("", 'a'), 0); // Returns N-1 (which is 0 for empty string) - + // Find in first position EXPECT_EQ(find("Hello", 'H'), 0); - + // Find in last position EXPECT_EQ(find("Hello", 'o'), 4); } @@ -267,13 +267,13 @@ TEST_F(CStringTest, Find) { TEST_F(CStringTest, Length) { // Basic length EXPECT_EQ(length("Hello"), 5); - + // Empty string EXPECT_EQ(length(""), 0); - + // String with spaces EXPECT_EQ(length("Hello World"), 11); - + // String with special characters EXPECT_EQ(length("Hello\nWorld"), 11); } @@ -283,19 +283,19 @@ TEST_F(CStringTest, Reverse) { // Basic reversal auto result1 = reverse("Hello"); EXPECT_EQ(arrayToString(result1), "olleH"); - + // Palindrome auto result2 = reverse("racecar"); EXPECT_EQ(arrayToString(result2), "racecar"); - + // Empty string auto result3 = reverse(""); EXPECT_EQ(arrayToString(result3), ""); - + // Single character auto result4 = reverse("A"); EXPECT_EQ(arrayToString(result4), "A"); - + // String with spaces auto result5 = reverse("Hello World"); EXPECT_EQ(arrayToString(result5), "dlroW olleH"); @@ -306,27 +306,27 @@ TEST_F(CStringTest, TrimStringView) { // Basic trimming std::string_view sv = " Hello "; EXPECT_EQ(trim(sv), "Hello"); - + // No spaces to trim sv = "Hello"; EXPECT_EQ(trim(sv), "Hello"); - + // Only leading spaces sv = " Hello"; EXPECT_EQ(trim(sv), "Hello"); - + // Only trailing spaces sv = "Hello "; EXPECT_EQ(trim(sv), "Hello"); - + // Only spaces sv = " "; EXPECT_EQ(trim(sv), ""); - + // Empty string sv = ""; EXPECT_EQ(trim(sv), ""); - + // All types of whitespace sv = " \t\n\r\fHello\v \t"; EXPECT_EQ(trim(sv), "Hello"); @@ -338,12 +338,12 @@ TEST_F(CStringTest, CharArrayConversion) { std::array input1 = {'H', 'e', 'l', 'l', 'o', '\0'}; auto result1 = charArrayToArrayConstexpr(input1); EXPECT_EQ(arrayToString(result1), "Hello"); - + // Test charArrayToArray std::array input2 = {'W', 'o', 'r', 'l', 'd', '\0'}; auto result2 = charArrayToArray(input2); EXPECT_EQ(arrayToString(result2), "World"); - + // Empty array std::array emptyArray = {'\0'}; auto result3 = charArrayToArrayConstexpr(emptyArray); @@ -355,15 +355,15 @@ TEST_F(CStringTest, IsNegative) { // Negative number std::array negative = {'-', '1', '\0'}; EXPECT_TRUE(isNegative(negative)); - + // Positive number std::array positive = {'4', '2', '\0'}; EXPECT_FALSE(isNegative(positive)); - + // Zero std::array zero = {'0', '\0'}; EXPECT_FALSE(isNegative(zero)); - + // Empty array std::array empty = {'\0'}; EXPECT_FALSE(isNegative(empty)); @@ -374,19 +374,19 @@ TEST_F(CStringTest, ArrayToInt) { // Basic conversion std::array num1 = {'1', '2', '3', '\0'}; EXPECT_EQ(arrayToInt(num1), 123); - + // Negative number std::array num2 = {'-', '4', '5', '\0'}; EXPECT_EQ(arrayToInt(num2), -45); - + // Leading zeros std::array num3 = {'0', '0', '4', '2', '\0'}; EXPECT_EQ(arrayToInt(num3), 42); - + // Binary base std::array bin = {'1', '0', '1', '0', '1', '\0'}; EXPECT_EQ(arrayToInt(bin, BASE_2), 21); // 10101 in binary is 21 in decimal - + // Hexadecimal base std::array hex = {'F', 'F', 'F', '\0'}; EXPECT_EQ(arrayToInt(hex, BASE_16), 4095); // FFF in hex is 4095 in decimal @@ -397,11 +397,11 @@ TEST_F(CStringTest, AbsoluteValue) { // Positive number std::array pos = {'4', '2', '\0'}; EXPECT_EQ(absoluteValue(pos), 42); - + // Negative number std::array neg = {'-', '4', '2', '\0'}; EXPECT_EQ(absoluteValue(neg), 42); - + // Zero std::array zero = {'0', '\0'}; EXPECT_EQ(absoluteValue(zero), 0); @@ -412,23 +412,23 @@ TEST_F(CStringTest, ConvertBase) { // Decimal to binary std::array dec1 = {'1', '0', '\0'}; EXPECT_EQ(convertBase(dec1, BASE_10, BASE_2), "1010"); // 10 to binary - + // Decimal to hex std::array dec2 = {'2', '5', '5', '\0'}; EXPECT_EQ(convertBase(dec2, BASE_10, BASE_16), "FF"); // 255 to hex - + // Binary to decimal std::array bin = {'1', '0', '1', '0', '1', '\0'}; EXPECT_EQ(convertBase(bin, BASE_2, BASE_10), "21"); // 10101 binary to decimal - + // Hex to decimal std::array hex = {'F', 'F', '\0'}; EXPECT_EQ(convertBase(hex, BASE_16, BASE_10), "255"); // FF to decimal - + // Zero conversion std::array zero = {'0', '\0'}; EXPECT_EQ(convertBase(zero, BASE_10, BASE_16), "0"); - + // Negative number std::array neg = {'-', '5', '\0'}; EXPECT_EQ(convertBase(neg, BASE_10, BASE_2), "-101"); // -5 to binary @@ -437,7 +437,7 @@ TEST_F(CStringTest, ConvertBase) { // Test compile-time capabilities TEST_F(CStringTest, CompileTimeOperations) { // These tests verify that the functions work at compile time - + // Create compile-time constants constexpr auto deduped = deduplicate("hello"); constexpr auto replaced = replace("hello", 'l', 'x'); @@ -448,7 +448,7 @@ TEST_F(CStringTest, CompileTimeOperations) { constexpr auto found = find("Hello", 'e'); constexpr auto len = length("Hello"); constexpr bool isEqual = equal("Hello", "Hello"); - + // Verify values EXPECT_EQ(arrayToString(deduped), "helo"); EXPECT_EQ(arrayToString(replaced), "hexxo"); @@ -470,14 +470,14 @@ TEST_F(CStringTest, ComplexCombinations) { constexpr auto step2 = replace(step1_str, ' ', '_'); constexpr const char step2_str[] = "hello_world"; constexpr auto step3 = reverse(step2_str); - + EXPECT_EQ(arrayToString(step3), "dlrow_olleh"); - + // Test with various special characters constexpr const char specialChars[] = "!@#$%^&*()_+{}:<>?"; auto revSpecial = reverse(specialChars); EXPECT_EQ(arrayToString(revSpecial), "?><:{}+_)(*&^%$#@!"); - + // Unicode handling is limited in C-style strings, so these tests are basic constexpr const char unicodeChars[] = "Привет"; // Russian word "hello" auto revUnicode = reverse(unicodeChars); @@ -488,4 +488,4 @@ TEST_F(CStringTest, ComplexCombinations) { } // namespace atom::utils::test -#endif // ATOM_UTILS_TEST_CSTRING_HPP \ No newline at end of file +#endif // ATOM_UTILS_TEST_CSTRING_HPP diff --git a/tests/utils/test_difflib.hpp b/tests/utils/test_difflib.hpp index 7fcb3de2..67f6ef12 100644 --- a/tests/utils/test_difflib.hpp +++ b/tests/utils/test_difflib.hpp @@ -450,4 +450,4 @@ inline void printMatchingBlocks( } // namespace atom::utils::test -#endif // ATOM_UTILS_TEST_DIFFLIB_HPP \ No newline at end of file +#endif // ATOM_UTILS_TEST_DIFFLIB_HPP diff --git a/tests/utils/test_lcg.hpp b/tests/utils/test_lcg.hpp index 159f8d83..82064c06 100644 --- a/tests/utils/test_lcg.hpp +++ b/tests/utils/test_lcg.hpp @@ -620,4 +620,4 @@ TEST_F(LCGTest, ThreadSafety) { } } -} // namespace atom::utils::tests \ No newline at end of file +} // namespace atom::utils::tests diff --git a/tests/utils/test_linq.hpp b/tests/utils/test_linq.hpp index e14fd826..1cc1be8e 100644 --- a/tests/utils/test_linq.hpp +++ b/tests/utils/test_linq.hpp @@ -579,4 +579,4 @@ TEST_F(LinqTest, NullPredicateHandling) { } */ -} // namespace atom::utils::tests \ No newline at end of file +} // namespace atom::utils::tests diff --git a/tests/utils/test_print.hpp b/tests/utils/test_print.hpp index c77418da..25b35291 100644 --- a/tests/utils/test_print.hpp +++ b/tests/utils/test_print.hpp @@ -612,4 +612,4 @@ TEST_F(PrintUtilsTest, MemoryTracker) { << "Buffer2 should still be reported"; } -} // namespace atom::utils::tests \ No newline at end of file +} // namespace atom::utils::tests diff --git a/tests/utils/test_qdatetime.hpp b/tests/utils/test_qdatetime.hpp index 9ab1a2d9..0c8c7ceb 100644 --- a/tests/utils/test_qdatetime.hpp +++ b/tests/utils/test_qdatetime.hpp @@ -251,4 +251,4 @@ TEST_F(QDateTimeTest, SecsToIntegratedTest) { << "Direct difference should equal sum of segment differences"; } -} // namespace atom::utils::tests \ No newline at end of file +} // namespace atom::utils::tests diff --git a/tests/utils/test_qprocess.hpp b/tests/utils/test_qprocess.hpp index d4b63327..f714973a 100644 --- a/tests/utils/test_qprocess.hpp +++ b/tests/utils/test_qprocess.hpp @@ -346,4 +346,4 @@ TEST_F(QProcessTerminateTest, TerminateWithCustomWorkingDirectory) { // 进程应该被终止 std::this_thread::sleep_for(std::chrono::milliseconds(100)); EXPECT_FALSE(process->isRunning()); -} \ No newline at end of file +} diff --git a/tests/utils/test_qtimer.hpp b/tests/utils/test_qtimer.hpp index b4d0fb2e..b00319b5 100644 --- a/tests/utils/test_qtimer.hpp +++ b/tests/utils/test_qtimer.hpp @@ -306,4 +306,4 @@ TEST_F(ElapsedTimerTest, StartHandlesExceptions) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/utils/test_random.hpp b/tests/utils/test_random.hpp index a29067c3..101f3db3 100644 --- a/tests/utils/test_random.hpp +++ b/tests/utils/test_random.hpp @@ -585,4 +585,4 @@ TEST_F(RandomTest, GenerateRandomStringPerformance) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/utils/test_switch.hpp b/tests/utils/test_switch.hpp index 0e5ee57b..d85ba0ca 100644 --- a/tests/utils/test_switch.hpp +++ b/tests/utils/test_switch.hpp @@ -191,4 +191,4 @@ TEST_F(StringSwitchTest, DifferentReturnTypes) { } // namespace atom::utils::test -#endif // ATOM_UTILS_TEST_SWITCH_HPP \ No newline at end of file +#endif // ATOM_UTILS_TEST_SWITCH_HPP diff --git a/tests/utils/test_time.hpp b/tests/utils/test_time.hpp index 081c6ba6..ab01a83a 100644 --- a/tests/utils/test_time.hpp +++ b/tests/utils/test_time.hpp @@ -411,4 +411,4 @@ TEST_F(TimeUtilsTest, TimestampWithMilliseconds) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/utils/test_to_byte.hpp b/tests/utils/test_to_byte.hpp index fcf6e6fd..d6206fc1 100644 --- a/tests/utils/test_to_byte.hpp +++ b/tests/utils/test_to_byte.hpp @@ -555,4 +555,4 @@ TEST_F(SerializationTest, PartialDeserialization) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/utils/test_to_string.hpp b/tests/utils/test_to_string.hpp index 53ee6d44..5ea06734 100644 --- a/tests/utils/test_to_string.hpp +++ b/tests/utils/test_to_string.hpp @@ -29,7 +29,7 @@ class StreamableClass { public: int value; explicit StreamableClass(int val) : value(val) {} - + friend std::ostream& operator<<(std::ostream& os, const StreamableClass& obj) { os << "StreamableClass(" << obj.value << ")"; return os; @@ -51,7 +51,7 @@ class ToStringTest : public ::testing::Test { void SetUp() override { // Setup test environment } - + void TearDown() override { // Clean up test environment } @@ -62,23 +62,23 @@ TEST_F(ToStringTest, StringTypes) { // std::string std::string str = "hello"; EXPECT_EQ(toString(str), "hello"); - + // const char* const char* cstr = "hello"; EXPECT_EQ(toString(cstr), "hello"); - + // char* char mutable_str[] = "hello"; EXPECT_EQ(toString(mutable_str), "hello"); - + // std::string_view std::string_view str_view = "hello"; EXPECT_EQ(toString(str_view), "hello"); - + // Null C-string pointer const char* null_str = nullptr; EXPECT_EQ(toString(null_str), "null"); - + // Empty string EXPECT_EQ(toString(""), ""); } @@ -102,14 +102,14 @@ TEST_F(ToStringTest, PointerType) { int value = 42; int* ptr = &value; std::string result = toString(ptr); - + EXPECT_THAT(result, StartsWith("Pointer(")); EXPECT_THAT(result, HasSubstr("42")); - + // Null pointer int* null_ptr = nullptr; EXPECT_EQ(toString(null_ptr), "nullptr"); - + // Pointer to complex type std::string str = "test"; std::string* str_ptr = &str; @@ -121,14 +121,14 @@ TEST_F(ToStringTest, PointerType) { TEST_F(ToStringTest, SmartPointerType) { auto shared_ptr = std::make_shared(42); std::string result = toString(shared_ptr); - + EXPECT_THAT(result, StartsWith("SmartPointer(")); EXPECT_THAT(result, HasSubstr("42")); - + // Null smart pointer std::shared_ptr null_shared_ptr; EXPECT_EQ(toString(null_shared_ptr), "nullptr"); - + // Unique pointer auto unique_ptr = std::make_unique(123); result = toString(unique_ptr); @@ -140,20 +140,20 @@ TEST_F(ToStringTest, SmartPointerType) { TEST_F(ToStringTest, VectorContainer) { std::vector vec = {1, 2, 3, 4, 5}; std::string result = toString(vec); - + EXPECT_EQ(result, "[1, 2, 3, 4, 5]"); - + // Empty vector std::vector empty_vec; EXPECT_EQ(toString(empty_vec), "[]"); - + // Vector with custom separator EXPECT_EQ(toString(vec, " | "), "[1 | 2 | 3 | 4 | 5]"); - + // Vector of strings std::vector str_vec = {"hello", "world"}; EXPECT_EQ(toString(str_vec), "[hello, world]"); - + // Nested vector std::vector> nested_vec = {{1, 2}, {3, 4}}; EXPECT_EQ(toString(nested_vec), "[[1, 2], [3, 4]]"); @@ -163,7 +163,7 @@ TEST_F(ToStringTest, VectorContainer) { TEST_F(ToStringTest, ListContainer) { std::list list = {1, 2, 3, 4, 5}; EXPECT_EQ(toString(list), "[1, 2, 3, 4, 5]"); - + // Empty list std::list empty_list; EXPECT_EQ(toString(empty_list), "[]"); @@ -174,7 +174,7 @@ TEST_F(ToStringTest, SetContainer) { std::set set = {5, 3, 1, 4, 2}; // Set will be ordered EXPECT_EQ(toString(set), "[1, 2, 3, 4, 5]"); - + // Empty set std::set empty_set; EXPECT_EQ(toString(empty_set), "[]"); @@ -184,27 +184,27 @@ TEST_F(ToStringTest, SetContainer) { TEST_F(ToStringTest, MapType) { std::map map = {{1, "one"}, {2, "two"}, {3, "three"}}; std::string result = toString(map); - + EXPECT_EQ(result, "{1: one, 2: two, 3: three}"); - + // Empty map std::map empty_map; EXPECT_EQ(toString(empty_map), "{}"); - + // Map with custom separator EXPECT_EQ(toString(map, " | "), "{1: one | 2: two | 3: three}"); - + // Map with string keys std::map str_map = {{"one", 1}, {"two", 2}, {"three", 3}}; EXPECT_EQ(toString(str_map), "{one: 1, three: 3, two: 2}"); - + // Nested map std::map> nested_map = { {1, {{1, "one-one"}, {2, "one-two"}}}, {2, {{1, "two-one"}, {2, "two-two"}}} }; EXPECT_EQ(toString(nested_map), "{1: {1: one-one, 2: one-two}, 2: {1: two-one, 2: two-two}}"); - + // Unordered map (order is not guaranteed, so just check length and specific elements) std::unordered_map umap = {{1, "one"}, {2, "two"}, {3, "three"}}; result = toString(umap); @@ -219,7 +219,7 @@ TEST_F(ToStringTest, MapType) { TEST_F(ToStringTest, ArrayType) { std::array arr = {1, 2, 3, 4, 5}; EXPECT_EQ(toString(arr), "[1, 2, 3, 4, 5]"); - + // Empty array std::array empty_arr = {}; EXPECT_EQ(toString(empty_arr), "[]"); @@ -229,18 +229,18 @@ TEST_F(ToStringTest, ArrayType) { TEST_F(ToStringTest, TupleType) { auto tuple = std::make_tuple(1, "hello", 3.14); EXPECT_EQ(toString(tuple), "(1, hello, 3.140000)"); - + // Empty tuple auto empty_tuple = std::make_tuple(); EXPECT_EQ(toString(empty_tuple), "()"); - + // Single element tuple auto single_tuple = std::make_tuple(42); EXPECT_EQ(toString(single_tuple), "(42)"); - + // Tuple with custom separator EXPECT_EQ(toString(tuple, " - "), "(1 - hello - 3.140000)"); - + // Nested tuple auto nested_tuple = std::make_tuple(std::make_tuple(1, 2), std::make_tuple("a", "b")); EXPECT_EQ(toString(nested_tuple), "((1, 2), (a, b))"); @@ -250,11 +250,11 @@ TEST_F(ToStringTest, TupleType) { TEST_F(ToStringTest, OptionalType) { std::optional opt = 42; EXPECT_EQ(toString(opt), "Optional(42)"); - + // Empty optional std::optional empty_opt; EXPECT_EQ(toString(empty_opt), "nullopt"); - + // Optional with complex type std::optional> opt_vec = std::vector{1, 2, 3}; EXPECT_EQ(toString(opt_vec), "Optional([1, 2, 3])"); @@ -264,13 +264,13 @@ TEST_F(ToStringTest, OptionalType) { TEST_F(ToStringTest, VariantType) { std::variant var = 42; EXPECT_EQ(toString(var), "42"); - + var = "hello"; EXPECT_EQ(toString(var), "hello"); - + var = 3.14; EXPECT_EQ(toString(var), "3.140000"); - + // Variant with complex type std::variant> var2 = std::vector{1, 2, 3}; EXPECT_EQ(toString(var2), "[1, 2, 3]"); @@ -281,11 +281,11 @@ TEST_F(ToStringTest, GeneralTypesStdToString) { // Integer EXPECT_EQ(toString(42), "42"); EXPECT_EQ(toString(-42), "-42"); - + // Float/Double EXPECT_EQ(toString(3.14f), "3.140000"); EXPECT_EQ(toString(-3.14), "-3.140000"); - + // Boolean EXPECT_EQ(toString(true), "1"); EXPECT_EQ(toString(false), "0"); @@ -305,16 +305,16 @@ TEST_F(ToStringTest, ErrorHandling) { nullptr, std::make_shared(3) }; - + std::string result = toString(vec); EXPECT_THAT(result, HasSubstr("[SmartPointer")); EXPECT_THAT(result, HasSubstr("nullptr")); - + // Exception in conversion should be caught and reported try { // This would cause a static_assert failure in actual code //toString(NonStreamableClass(42)); - + // Instead, simulate a conversion error throw ToStringException("Test exception"); } catch (const ToStringException& e) { @@ -326,17 +326,17 @@ TEST_F(ToStringTest, ErrorHandling) { // Test toStringArray function TEST_F(ToStringTest, ToStringArray) { std::vector vec = {1, 2, 3, 4, 5}; - + // Default separator (space) EXPECT_EQ(toStringArray(vec), "1 2 3 4 5"); - + // Custom separator EXPECT_EQ(toStringArray(vec, ", "), "1, 2, 3, 4, 5"); - + // Empty array std::vector empty_vec; EXPECT_EQ(toStringArray(empty_vec), ""); - + // Array with complex types std::vector> nested_vec = {{1, 2}, {3, 4}}; EXPECT_EQ(toStringArray(nested_vec), "[1, 2] [3, 4]"); @@ -345,16 +345,16 @@ TEST_F(ToStringTest, ToStringArray) { // Test toStringRange function TEST_F(ToStringTest, ToStringRange) { std::vector vec = {1, 2, 3, 4, 5}; - + // Default separator EXPECT_EQ(toStringRange(vec.begin(), vec.end()), "[1, 2, 3, 4, 5]"); - + // Custom separator EXPECT_EQ(toStringRange(vec.begin(), vec.end(), " | "), "[1 | 2 | 3 | 4 | 5]"); - + // Empty range EXPECT_EQ(toStringRange(vec.begin(), vec.begin()), "[]"); - + // Partial range EXPECT_EQ(toStringRange(vec.begin() + 1, vec.begin() + 4), "[2, 3, 4]"); } @@ -363,13 +363,13 @@ TEST_F(ToStringTest, ToStringRange) { TEST_F(ToStringTest, JoinCommandLine) { // Basic join EXPECT_EQ(joinCommandLine("program", "-f", "file.txt"), "program -f file.txt"); - + // Join with mixed types EXPECT_EQ(joinCommandLine("program", 42, 3.14, true), "program 42 3.140000 1"); - + // Join with no arguments EXPECT_EQ(joinCommandLine(), ""); - + // Join with single argument EXPECT_EQ(joinCommandLine("program"), "program"); } @@ -378,7 +378,7 @@ TEST_F(ToStringTest, JoinCommandLine) { TEST_F(ToStringTest, DequeContainer) { std::deque deq = {1, 2, 3, 4, 5}; EXPECT_EQ(toString(deq), "[1, 2, 3, 4, 5]"); - + // Empty deque std::deque empty_deq; EXPECT_EQ(toString(empty_deq), "[]"); @@ -388,10 +388,10 @@ TEST_F(ToStringTest, DequeContainer) { TEST_F(ToStringTest, CustomDelimiters) { std::vector vec = {1, 2, 3}; EXPECT_EQ(toString(vec, " -> "), "[1 -> 2 -> 3]"); - + std::map map = {{1, "one"}, {2, "two"}}; EXPECT_EQ(toString(map, " => "), "{1: one => 2: two}"); - + auto tuple = std::make_tuple(1, "hello", 3.14); EXPECT_EQ(toString(tuple, "; "), "(1; hello; 3.140000)"); } @@ -404,7 +404,7 @@ TEST_F(ToStringTest, NestedComplexStructures) { {2, {4, 5, 6}} }; EXPECT_EQ(toString(map_of_vecs), "{1: [1, 2, 3], 2: [4, 5, 6]}"); - + // Vector of optionals std::vector> vec_of_opts = { std::optional{1}, @@ -412,15 +412,15 @@ TEST_F(ToStringTest, NestedComplexStructures) { std::optional{3} }; EXPECT_EQ(toString(vec_of_opts), "[Optional(1), nullopt, Optional(3)]"); - + // Optional of vector std::optional> opt_vec = std::vector{1, 2, 3}; EXPECT_EQ(toString(opt_vec), "Optional([1, 2, 3])"); - + // Variant of container std::variant> var_vec = std::vector{1, 2, 3}; EXPECT_EQ(toString(var_vec), "[1, 2, 3]"); - + // Tuple with complex elements auto complex_tuple = std::make_tuple( std::vector{1, 2, 3}, @@ -434,15 +434,15 @@ TEST_F(ToStringTest, NestedComplexStructures) { TEST_F(ToStringTest, PointersToContainers) { auto vec_ptr = std::make_shared>(std::vector{1, 2, 3}); std::string result = toString(vec_ptr); - + EXPECT_THAT(result, StartsWith("SmartPointer(")); EXPECT_THAT(result, HasSubstr("[1, 2, 3]")); - + // Raw pointer to container std::vector vec = {1, 2, 3}; std::vector* raw_ptr = &vec; result = toString(raw_ptr); - + EXPECT_THAT(result, StartsWith("Pointer(")); EXPECT_THAT(result, HasSubstr("[1, 2, 3]")); } @@ -455,7 +455,7 @@ TEST_F(ToStringTest, ErrorInContainers) { nullptr, std::make_shared(3) }; - + std::string result = toString(vec); EXPECT_THAT(result, HasSubstr("[SmartPointer")); EXPECT_THAT(result, HasSubstr("nullptr")); @@ -478,20 +478,20 @@ TEST_F(ToStringTest, RecursiveStructures) { int value; std::shared_ptr next; }; - + auto node1 = std::make_shared(); auto node2 = std::make_shared(); auto node3 = std::make_shared(); - + node1->value = 1; node1->next = node2; - + node2->value = 2; node2->next = node3; - + node3->value = 3; node3->next = nullptr; - + std::string result = toString(node1); EXPECT_THAT(result, HasSubstr("SmartPointer")); EXPECT_THAT(result, HasSubstr("1")); @@ -507,18 +507,18 @@ TEST_F(ToStringTest, LargeStructurePerformance) { for (int i = 0; i < 10000; i++) { large_vec[i] = i; } - + // Measure time to convert to string auto start = std::chrono::high_resolution_clock::now(); std::string result = toString(large_vec); auto end = std::chrono::high_resolution_clock::now(); - + auto duration_ms = std::chrono::duration_cast(end - start).count(); - + // Verify correct conversion EXPECT_THAT(result, StartsWith("[0, 1, 2")); EXPECT_THAT(result, EndsWith("9998, 9999]")); - + // Just log performance - no strict assertion as it will vary by system std::cout << "Converted vector of 10000 elements in " << duration_ms << "ms" << std::endl; } @@ -527,18 +527,18 @@ TEST_F(ToStringTest, LargeStructurePerformance) { TEST_F(ToStringTest, ContainerAdaptors) { // stack, queue, and priority_queue don't satisfy the Container concept directly // but their underlying containers do when accessed properly - + // For testing purposes, we can use a custom adaptor wrapper std::vector vec = {1, 2, 3, 4, 5}; - + // Test with a custom wrapper that simulates container adaptors struct AdaptorWrapper { std::vector& container; - + auto begin() const { return container.begin(); } auto end() const { return container.end(); } }; - + AdaptorWrapper wrapper{vec}; EXPECT_EQ(toString(wrapper), "[1, 2, 3, 4, 5]"); } @@ -560,9 +560,9 @@ TEST_F(ToStringTest, RealWorldExample) { {"absent", std::nullopt} }} }; - + std::string result = toString(complex_data); - + // Check for key components in the result EXPECT_THAT(result, HasSubstr("int_value: 42")); EXPECT_THAT(result, HasSubstr("string_value: hello world")); @@ -575,4 +575,4 @@ TEST_F(ToStringTest, RealWorldExample) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/utils/test_uuid.cpp b/tests/utils/test_uuid.cpp index 3f2a194d..a72fd5fe 100644 --- a/tests/utils/test_uuid.cpp +++ b/tests/utils/test_uuid.cpp @@ -670,4 +670,4 @@ TEST_F(FastUUIDTest, HashMethod) { EXPECT_EQ(uuidMap[uuid3], 3); } -#endif \ No newline at end of file +#endif diff --git a/tests/utils/test_valid_string.hpp b/tests/utils/test_valid_string.hpp index 9d8aeeae..8762a6c2 100644 --- a/tests/utils/test_valid_string.hpp +++ b/tests/utils/test_valid_string.hpp @@ -537,9 +537,9 @@ TEST_F(ValidStringTest, ProgrammingSyntax) { // SQL like syntax std::string sqlCode = R"( - SELECT * FROM users + SELECT * FROM users WHERE (age > 18) AND ( - status = 'active' OR + status = 'active' OR (registration_date > '2023-01-01') ) )"; diff --git a/tests/utils/test_xml.hpp b/tests/utils/test_xml.hpp index 68255f11..aeb6fc47 100644 --- a/tests/utils/test_xml.hpp +++ b/tests/utils/test_xml.hpp @@ -570,4 +570,4 @@ TEST_F(XMLReaderTest, ComplexXML) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/tests/web/test_address.hpp b/tests/web/test_address.hpp index 6d899370..4bbefe97 100644 --- a/tests/web/test_address.hpp +++ b/tests/web/test_address.hpp @@ -39,22 +39,22 @@ TEST_F(UnixDomainTest, GetBroadcastAddressLogsWarning) { logOutput = message.message; return false; }); - + // Redirect the ERROR messages to our capture handler loguru::g_stderr_verbosity = loguru::Verbosity_OFF; - loguru::add_callback("test_callback", + loguru::add_callback("test_callback", [&logOutput](void*, const loguru::Message& message) { logOutput = message.message; - }, + }, loguru::Verbosity_WARNING); - + // Execute the method that should log a warning UnixDomain unixDomain("/tmp/test.sock"); unixDomain.getBroadcastAddress("255.255.255.0"); - + // Check if the log contains the expected warning message EXPECT_THAT(logOutput, HasSubstr("getBroadcastAddress operation not applicable for Unix domain sockets")); - + // Clean up loguru::remove_callback("test_callback"); } @@ -62,7 +62,7 @@ TEST_F(UnixDomainTest, GetBroadcastAddressLogsWarning) { // Test getBroadcastAddress with different types of masks TEST_F(UnixDomainTest, GetBroadcastAddressWithDifferentMasks) { UnixDomain unixDomain("/tmp/test.sock"); - + // Test with various mask formats EXPECT_TRUE(unixDomain.getBroadcastAddress("").empty()); EXPECT_TRUE(unixDomain.getBroadcastAddress("255.255.255.0").empty()); @@ -76,7 +76,7 @@ TEST_F(UnixDomainTest, GetBroadcastAddressWithLongPath) { // Create a Unix domain socket with a long path (but still valid) std::string longPath = "/tmp/" + std::string(90, 'a'); UnixDomain unixDomain(longPath); - + EXPECT_TRUE(unixDomain.getBroadcastAddress("255.255.255.0").empty()); } @@ -86,11 +86,11 @@ TEST_F(UnixDomainTest, GetBroadcastAddressAfterDifferentConstructions) { UnixDomain unixDomain1; unixDomain1.parse("/tmp/test1.sock"); EXPECT_TRUE(unixDomain1.getBroadcastAddress("255.255.255.0").empty()); - + // Direct construction with path UnixDomain unixDomain2("/tmp/test2.sock"); EXPECT_TRUE(unixDomain2.getBroadcastAddress("255.255.255.0").empty()); - + // Copy construction UnixDomain unixDomain3(unixDomain2); EXPECT_TRUE(unixDomain3.getBroadcastAddress("255.255.255.0").empty()); @@ -99,15 +99,15 @@ TEST_F(UnixDomainTest, GetBroadcastAddressAfterDifferentConstructions) { // Test interaction between getBroadcastAddress and other methods TEST_F(UnixDomainTest, GetBroadcastAddressInteractionWithOtherMethods) { UnixDomain unixDomain("/tmp/test.sock"); - + // Call other methods before getBroadcastAddress unixDomain.getType(); unixDomain.toBinary(); unixDomain.toHex(); - + // getBroadcastAddress should still return empty string EXPECT_TRUE(unixDomain.getBroadcastAddress("255.255.255.0").empty()); - + // Call methods after getBroadcastAddress EXPECT_EQ(unixDomain.getType(), "UnixDomain"); EXPECT_FALSE(unixDomain.toBinary().empty()); @@ -120,14 +120,14 @@ TEST_F(UnixDomainTest, CompareBroadcastAddressBehaviorWithOtherTypes) { UnixDomain unixDomain("/tmp/test.sock"); IPv4 ipv4("192.168.1.1"); IPv6 ipv6("2001:db8::1"); - + // For Unix domain sockets, getBroadcastAddress should return an empty string EXPECT_TRUE(unixDomain.getBroadcastAddress("255.255.255.0").empty()); - + // For IPv4, getBroadcastAddress should return a valid address EXPECT_FALSE(ipv4.getBroadcastAddress("255.255.255.0").empty()); EXPECT_EQ(ipv4.getBroadcastAddress("255.255.255.0"), "192.168.1.255"); - + // For IPv6, behavior depends on implementation details // (not testing exact result here since it's complex) EXPECT_NO_THROW(ipv6.getBroadcastAddress("ffff:ffff:ffff:ffff::")); @@ -139,7 +139,7 @@ TEST_F(UnixDomainTest, GetBroadcastAddressWithFactoryMethod) { auto address = Address::createFromString("/tmp/test.sock"); ASSERT_NE(address, nullptr); EXPECT_EQ(address->getType(), "UnixDomain"); - + // getBroadcastAddress should return an empty string EXPECT_TRUE(address->getBroadcastAddress("255.255.255.0").empty()); } @@ -153,13 +153,13 @@ TEST_F(UnixDomainTest, GetBroadcastAddressWithShortPath) { // Test multiple consecutive calls to getBroadcastAddress TEST_F(UnixDomainTest, MultipleBroadcastAddressCalls) { UnixDomain unixDomain("/tmp/test.sock"); - + // Call getBroadcastAddress multiple times consecutively EXPECT_TRUE(unixDomain.getBroadcastAddress("255.255.255.0").empty()); EXPECT_TRUE(unixDomain.getBroadcastAddress("255.255.0.0").empty()); EXPECT_TRUE(unixDomain.getBroadcastAddress("255.0.0.0").empty()); EXPECT_TRUE(unixDomain.getBroadcastAddress("0.0.0.0").empty()); - + // Verify the socket path is unchanged EXPECT_EQ(unixDomain.getAddress(), "/tmp/test.sock"); -} \ No newline at end of file +} diff --git a/tests/web/test_httpparser.hpp b/tests/web/test_httpparser.hpp index a341b41a..9cc7ae9b 100644 --- a/tests/web/test_httpparser.hpp +++ b/tests/web/test_httpparser.hpp @@ -180,4 +180,4 @@ TEST_F(HttpHeaderParserTest, BuildResponseWithBody) { EXPECT_EQ(newParser.getBody(), htmlBody); } -#endif // TEST_HTTPPARSER_HPP \ No newline at end of file +#endif // TEST_HTTPPARSER_HPP diff --git a/tests/web/test_minetype.hpp b/tests/web/test_minetype.hpp index a96a0f2b..2dd3335e 100644 --- a/tests/web/test_minetype.hpp +++ b/tests/web/test_minetype.hpp @@ -375,4 +375,4 @@ TEST_F(MimeTypesTest, LenientMode) { }); } -#endif // TEST_MINETYPE_HPP \ No newline at end of file +#endif // TEST_MINETYPE_HPP diff --git a/tests/xmake.lua b/tests/xmake.lua index 3fd79724..485f8ac2 100644 --- a/tests/xmake.lua +++ b/tests/xmake.lua @@ -22,22 +22,22 @@ function add_atom_test(name, files) target("test_" .. name) -- Set target kind to executable set_kind("binary") - + -- Add group for testing set_group("tests") - + -- Add source files add_files(files) - + -- Add dependencies add_deps("atom") - + -- Add packages add_packages("gtest", "loguru") - + -- Output directory set_targetdir("$(buildir)/tests") - + -- Set test target attributes on_run(function(target) os.execv(target:targetfile()) @@ -56,7 +56,7 @@ end target("test") set_kind("phony") set_group("tests") - + on_run(function(target) -- Run all test targets local test_targets = target.project.targets diff --git a/vcpkg.json b/vcpkg.json index fe8cb5d5..612921c7 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -61,4 +61,4 @@ ] } } -} \ No newline at end of file +} diff --git a/xmake.lua b/xmake.lua index f1fa954c..04ef12f7 100644 --- a/xmake.lua +++ b/xmake.lua @@ -77,19 +77,19 @@ task("install") on_run(function() import("core.project.project") import("core.platform.platform") - + -- Set install prefix local prefix = option.get("prefix") or "/usr/local" - + -- Build the project os.exec("xmake build") - + -- Install the project os.exec("xmake install -o " .. prefix) - + cprint("${bright green}Atom has been installed to " .. prefix) end) - + set_menu { usage = "xmake install", description = "Install Atom libraries and headers" From 8e6b2fe0478af8c30f02cb891aa527d5fd4ffb17 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 17 Oct 2025 05:00:38 +0000 Subject: [PATCH 03/21] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .augment/rules/build-examples-fix.md | 1 + .augment/rules/file-and-code-management.md | 64 +- .../rules/information-verification-chains.md | 61 +- .augment/rules/update-examples.md | 1 + .augment/rules/update-python.md | 2 +- .augment/rules/update-tests.md | 1 + AGENTS.md | 6 + CLAUDE.md | 8 +- CMakeLists.txt | 334 +++---- README.md | 1 + WARP.md | 30 + atom/CMakeLists.txt | 443 +++++---- atom/algorithm/CMakeLists.txt | 36 +- atom/algorithm/compression/huffman.cpp | 4 +- .../algorithm/compression/matrix_compress.cpp | 13 +- atom/algorithm/core/algorithm.cpp | 4 +- atom/algorithm/core/algorithm.hpp | 4 +- atom/algorithm/core/opencl_utils.cpp | 74 +- atom/algorithm/core/opencl_utils.hpp | 122 +-- atom/algorithm/core/simd_utils.hpp | 144 +-- atom/algorithm/crypto/md5.hpp | 8 +- atom/algorithm/crypto/sha1.cpp | 3 +- atom/algorithm/crypto/tea.hpp | 36 +- atom/algorithm/encoding/base.cpp | 70 +- atom/algorithm/graphics/flood.hpp | 6 +- atom/algorithm/graphics/image_ops.hpp | 167 ++-- atom/algorithm/graphics/perlin.hpp | 5 +- atom/algorithm/graphics/simplex.hpp | 166 ++-- atom/algorithm/hash/mhash.cpp | 6 +- atom/algorithm/hash/mhash.hpp | 4 +- atom/algorithm/math/bignumber.hpp | 24 +- atom/algorithm/math/fraction.hpp | 4 +- atom/algorithm/math/gpu_math.cpp | 120 ++- atom/algorithm/math/gpu_math.hpp | 87 +- atom/algorithm/math/math.cpp | 6 +- atom/algorithm/math/math.hpp | 4 +- atom/algorithm/math/numerical.hpp | 143 +-- atom/algorithm/math/statistics.hpp | 164 ++-- atom/algorithm/optimization/annealing.hpp | 16 +- atom/algorithm/signal/convolve.cpp | 85 +- atom/algorithm/signal/convolve.hpp | 21 +- atom/algorithm/utils/error_calibration.hpp | 13 +- atom/algorithm/utils/fnmatch.hpp | 65 +- atom/algorithm/utils/snowflake.hpp | 4 +- atom/algorithm/utils/uuid.hpp | 130 +-- atom/algorithm/utils/weight.hpp | 14 +- atom/async/CMakeLists.txt | 25 +- atom/async/core/promise.hpp | 9 +- atom/async/execution/async_executor.hpp | 5 +- atom/async/execution/parallel.hpp | 34 +- atom/async/execution/pool.hpp | 13 +- atom/async/messaging/message_bus.hpp | 6 +- atom/async/messaging/message_queue.hpp | 6 +- atom/async/messaging/queue.hpp | 5 +- atom/async/sync/safetype.hpp | 6 +- atom/async/sync/slot.hpp | 10 +- atom/async/threading/lock.cpp | 10 +- atom/async/threading/thread_wrapper.hpp | 12 +- atom/async/threading/threadlocal.hpp | 14 +- atom/async/utils/daemon.hpp | 19 +- atom/async/utils/timer.cpp | 44 +- atom/async/utils/timer.hpp | 20 +- atom/components/CMakeLists.txt | 243 +++-- atom/components/component.template | 1 - atom/components/core/component.cpp | 2 +- atom/components/core/component.hpp | 36 +- atom/components/core/module_macro.hpp | 7 +- atom/components/core/registry.cpp | 9 +- atom/components/core/registry.hpp | 2 +- atom/components/data/serialization.hpp | 2 +- atom/components/data/var.cpp | 3 +- atom/components/lifecycle/dispatch.cpp | 34 +- atom/components/lifecycle/dispatch.hpp | 37 +- atom/components/lifecycle/iteration.hpp | 11 +- .../scripting/advanced_bindings.hpp | 2 +- atom/components/scripting/scripting_api.cpp | 2 +- atom/connection/CMakeLists.txt | 88 +- atom/connection/async_sockethub.cpp | 39 +- atom/connection/async_sockethub.hpp | 8 +- atom/connection/fifoclient.cpp | 5 +- atom/connection/fifoserver.hpp | 2 +- atom/connection/tcpclient.hpp | 3 +- atom/connection/udpclient.cpp | 18 +- atom/containers/CMakeLists.txt | 17 +- atom/containers/xmake.lua | 1 - atom/error/CMakeLists.txt | 104 +- atom/error/error_code.cpp | 220 +++-- atom/error/error_code.hpp | 138 ++- atom/error/error_context.cpp | 189 ++-- atom/error/error_context.hpp | 138 +-- atom/error/error_formatter.cpp | 387 +++++--- atom/error/error_formatter.hpp | 172 ++-- atom/error/error_handler.cpp | 173 ++-- atom/error/error_handler.hpp | 131 +-- atom/error/error_recovery.cpp | 304 +++--- atom/error/error_recovery.hpp | 196 ++-- atom/error/exception.hpp | 3 +- atom/error/stacktrace.cpp | 205 ++-- atom/error/stacktrace.hpp | 171 ++-- atom/extra/asio/CMakeLists.txt | 149 ++- atom/extra/asio/mqtt/packet.cpp | 6 +- atom/extra/asio/mqtt/test_packet.hpp | 1 - atom/extra/asio/mqtt/test_types.hpp | 1 - atom/extra/asio/sse/client/client.hpp | 2 +- atom/extra/asio/sse/client/client_config.cpp | 2 +- atom/extra/asio/sse/server/auth_service.cpp | 1 - atom/extra/asio/sse/server/connection.hpp | 1 - atom/extra/asio/sse/server/event_queue.hpp | 6 +- atom/extra/asio/sse/server/metrics.hpp | 1 - atom/extra/asio/sse/server/server_config.cpp | 1 - atom/extra/asio/sse/sse.hpp | 4 +- atom/extra/asio/xmake.lua | 17 +- atom/extra/beast/http.cpp | 80 +- atom/extra/beast/http.hpp | 12 +- atom/extra/beast/http_utils.hpp | 16 +- atom/extra/beast/ws.cpp | 3 +- atom/extra/beast/xmake.lua | 13 +- atom/extra/boost/uuid.hpp | 15 +- atom/extra/boost/xmake.lua | 11 +- atom/extra/curl/CMakeLists.txt | 30 +- atom/extra/curl/xmake.lua | 13 +- atom/extra/dotenv/CMakeLists.txt | 30 +- atom/extra/dotenv/parser.cpp | 3 +- atom/extra/dotenv/xmake.lua | 15 +- atom/extra/iconv/CMakeLists.txt | 30 +- atom/extra/iconv/xmake.lua | 11 +- atom/extra/inicpp/CMakeLists.txt | 16 +- atom/extra/inicpp/common.hpp | 4 +- atom/extra/inicpp/section.hpp | 2 - atom/extra/inicpp/xmake.lua | 9 +- atom/extra/injection/CMakeLists.txt | 17 +- atom/extra/injection/xmake.lua | 9 +- atom/extra/pugixml/xmake.lua | 11 +- atom/extra/spdlog/CMakeLists.txt | 39 +- atom/extra/spdlog/core/concepts.h | 1 - atom/extra/spdlog/core/error.h | 39 +- atom/extra/spdlog/core/test_context.h | 1 - atom/extra/spdlog/core/test_error.h | 1 - atom/extra/spdlog/events/event_system.h | 1 - .../extra/spdlog/events/test_event_system.cpp | 1 - atom/extra/spdlog/filters/builtin_filters.h | 1 - atom/extra/spdlog/logger/manager.cpp | 5 +- atom/extra/spdlog/logger/manager.h | 9 +- atom/extra/spdlog/logger/test_logger.cpp | 49 +- atom/extra/spdlog/logger/test_manager.cpp | 1 - atom/extra/spdlog/sampling/sampler.cpp | 3 +- .../spdlog/utils/test_structured_data.cpp | 1 - atom/extra/spdlog/utils/test_timer.cpp | 1 - atom/extra/spdlog/utils/timer.cpp | 1 - atom/extra/spdlog/utils/timer.h | 6 +- atom/extra/spdlog/xmake.lua | 13 +- atom/extra/uv/CMakeLists.txt | 67 +- atom/extra/uv/coro.hpp | 49 +- atom/extra/uv/message_bus.cpp | 12 +- atom/extra/uv/message_bus.hpp | 42 +- atom/extra/uv/xmake.lua | 15 +- atom/extra/xmake.lua | 9 +- atom/image/CMakeLists.txt | 177 ++-- atom/image/PERFORMANCE.md | 2 + atom/image/README.md | 12 + atom/image/build_minimal/build.ninja | 4 +- atom/image/core/README.md | 2 + atom/image/core/cache_manager.hpp | 2 +- atom/image/core/exceptions.hpp | 2 +- atom/image/core/image_blob.hpp | 78 +- atom/image/core/image_metadata.hpp | 129 ++- atom/image/formats/README.md | 12 + atom/image/formats/advanced_formats.cpp | 340 ++++--- atom/image/formats/advanced_formats.hpp | 231 +++-- atom/image/formats/fits_utils.cpp | 4 +- atom/image/image.hpp | 53 +- atom/image/io/README.md | 9 + atom/image/io/format_detector.cpp | 305 +++--- atom/image/io/image_loader.cpp | 198 ++-- atom/image/io/image_loader.hpp | 95 +- atom/image/io/image_saver.cpp | 308 +++--- atom/image/io/image_saver.hpp | 117 +-- atom/image/metadata/README.md | 8 + atom/image/metadata/exif.cpp | 20 +- atom/image/metadata/exif.hpp | 12 +- atom/image/processing/README.md | 2 + atom/image/processing/computer_vision.cpp | 413 ++++---- atom/image/processing/computer_vision.hpp | 228 ++--- atom/image/processing/enhancement.cpp | 540 ++++++----- atom/image/processing/enhancement.hpp | 222 ++--- atom/image/processing/filters.hpp | 107 +-- atom/image/processing/gpu_acceleration.cpp | 254 ++--- atom/image/processing/gpu_acceleration.hpp | 142 +-- atom/image/processing/image_processor.cpp | 111 ++- atom/image/processing/image_processor.hpp | 72 +- atom/image/processing/ml_processing.cpp | 406 ++++---- atom/image/processing/ml_processing.hpp | 256 ++--- atom/image/processing/ocr/ocr.cpp | 70 +- atom/image/processing/realtime.cpp | 217 +++-- atom/image/processing/realtime.hpp | 151 +-- atom/image/processing/realtime_stub.cpp | 79 +- atom/image/processing/transforms.hpp | 151 +-- atom/io/CMakeLists.txt | 114 +-- atom/io/async/async_compress.cpp | 15 +- atom/io/async/async_glob.cpp | 44 +- atom/io/async/async_glob.hpp | 2 - atom/io/async/async_io.cpp | 277 ++++-- atom/io/async/async_io.hpp | 359 ++++--- atom/io/compression/compress.cpp | 41 +- atom/io/compression/compress.hpp | 5 +- atom/io/core/io.cpp | 42 +- atom/io/core/io.hpp | 70 +- atom/io/core/path_utils.hpp | 34 +- atom/io/filesystem/file_info.cpp | 57 +- atom/io/filesystem/file_permission.cpp | 2 +- atom/log/CMakeLists.txt | 309 +++--- atom/log/async_logger.cpp | 7 +- atom/log/async_logger.hpp | 29 +- atom/log/atomlog.cpp | 9 +- atom/log/atomlog.hpp | 14 +- atom/log/cmake/loguru-cpack.cmake | 40 +- atom/log/cmake/utilities.cmake | 34 +- atom/log/loguru.cpp | 4 +- atom/log/loguru.hpp | 4 +- atom/log/mmap_logger.cpp | 11 +- atom/log/mmap_logger.hpp | 2 +- atom/memory/CMakeLists.txt | 41 +- atom/memory/memory.hpp | 17 +- atom/memory/memory_pool.hpp | 7 +- atom/memory/object.hpp | 6 +- atom/memory/shared.hpp | 80 +- atom/memory/tracker.cpp | 4 +- atom/meta/abi.hpp | 32 +- atom/meta/any.hpp | 4 +- atom/meta/anymeta.hpp | 4 +- atom/meta/awaitable.hpp | 4 +- atom/meta/constructor.hpp | 12 +- atom/meta/container_traits.hpp | 5 +- atom/meta/facade_any.hpp | 4 +- atom/meta/ffi.hpp | 31 +- atom/meta/func_traits.hpp | 12 +- atom/meta/global_ptr.hpp | 9 +- atom/meta/god.hpp | 45 +- atom/meta/invoke.hpp | 2 +- atom/meta/member.hpp | 23 +- atom/meta/property.hpp | 4 +- atom/meta/refl.hpp | 4 +- atom/meta/refl_yaml.hpp | 6 +- atom/meta/signature.hpp | 43 +- atom/meta/type_caster.hpp | 4 +- atom/meta/type_info.hpp | 11 +- atom/search/CMakeLists.txt | 19 +- atom/search/cache/cache.hpp | 11 +- atom/search/cache/lru.hpp | 54 +- atom/search/cache/ttl.hpp | 19 +- atom/search/database/sqlite.cpp | 37 +- atom/secret/CMakeLists.txt | 59 +- atom/secret/encryption.cpp | 301 +++--- atom/secret/encryption.hpp | 60 +- atom/secret/integration_test.cpp | 69 +- atom/secret/password_manager.cpp | 243 ++--- atom/secret/password_manager.hpp | 32 +- atom/secret/password_utils.cpp | 225 +++-- atom/secret/password_utils.hpp | 70 +- atom/secret/result.hpp | 12 +- atom/secret/serialization.cpp | 358 ++++--- atom/secret/serialization.hpp | 39 +- atom/serial/CMakeLists.txt | 110 +-- .../serial/bluetooth/bluetooth_serial_win.hpp | 125 +-- atom/sysinfo/hardware/battery.cpp | 10 +- atom/sysinfo/hardware/bios.cpp | 65 +- atom/sysinfo/hardware/cpu/freebsd.cpp | 220 +++-- atom/sysinfo/hardware/cpu/windows.cpp | 174 ++-- atom/sysinfo/hardware/memory/common.cpp | 4 +- atom/sysinfo/hardware/memory/linux.cpp | 13 +- atom/sysinfo/hardware/memory/memory.cpp | 34 +- atom/sysinfo/hardware/memory/windows.cpp | 85 +- atom/sysinfo/info/sn.cpp | 47 +- atom/sysinfo/network/wifi/CMakeLists.txt | 68 +- atom/sysinfo/network/wifi/common.cpp | 6 +- atom/sysinfo/network/wifi/linux.cpp | 2 +- atom/sysinfo/network/wifi/macos.cpp | 3 +- atom/sysinfo/network/wifi/wifi.cpp | 16 +- atom/sysinfo/network/wifi/windows.cpp | 1 - atom/sysinfo/storage/disk/disk_device.cpp | 3 +- atom/sysinfo/storage/disk/disk_device.hpp | 15 +- atom/sysinfo/storage/disk/disk_info.cpp | 6 +- atom/sysinfo/storage/disk/disk_monitor.cpp | 50 +- atom/sysinfo/storage/disk/disk_security.hpp | 4 +- atom/sysinfo/utils/sysinfo_printer.cpp | 5 +- atom/system/clipboard/clipboard_windows.cpp | 6 +- atom/system/core/priority.cpp | 3 +- atom/system/debug/crash_quotes.cpp | 5 +- atom/system/debug/crash_quotes.hpp | 5 +- atom/system/hardware/gpio.cpp | 19 +- atom/system/hardware/voltage_windows.cpp | 6 +- atom/system/info/env.cpp | 8 +- atom/system/info/env.hpp | 26 +- atom/system/info/software.cpp | 14 +- atom/system/info/stat.cpp | 112 +-- atom/system/info/user.cpp | 18 +- atom/system/info/user.hpp | 4 +- atom/system/network/virtual_network.cpp | 11 +- atom/system/process/command.cpp | 54 +- atom/system/process/command.hpp | 5 +- atom/system/process/process_info.hpp | 2 +- atom/system/process/process_manager.cpp | 39 +- atom/system/process/process_manager.hpp | 12 +- atom/system/registry/lregistry.cpp | 89 +- atom/system/registry/wregistry.cpp | 4 +- atom/system/registry/wregistry.hpp | 12 +- atom/system/scheduling/crontab.cpp | 12 +- atom/system/scheduling/crontab.hpp | 8 +- atom/system/shortcut/CMakeLists.txt | 112 +-- atom/system/signals/signal.cpp | 19 +- atom/tests/CMakeLists.txt | 67 +- atom/tests/charts.py | 7 +- atom/tests/fuzz.cpp | 26 +- atom/tests/test.hpp | 26 +- atom/tests/test_charts.py | 29 +- atom/type/CMakeLists.txt | 49 +- atom/type/argsview.hpp | 24 +- atom/type/compat.hpp | 30 +- atom/type/cstream.hpp | 7 +- atom/type/iter.hpp | 4 +- atom/type/qvariant.hpp | 9 +- atom/type/rjson.cpp | 62 +- atom/type/rjson.hpp | 8 +- atom/type/rtype.hpp | 12 +- atom/type/ryaml.cpp | 13 +- atom/type/small_vector.hpp | 4 +- atom/type/static_string.hpp | 22 +- atom/type/static_vector.hpp | 16 +- atom/type/string.hpp | 23 +- atom/type/weak_ptr.hpp | 9 +- atom/utils/CMakeLists.txt | 61 +- atom/utils/container/linq.hpp | 5 +- atom/utils/container/ranges.hpp | 5 +- atom/utils/container/span.hpp | 36 +- atom/utils/conversion/to_any.cpp | 14 +- atom/utils/conversion/to_any.hpp | 1 - atom/utils/conversion/to_byte.hpp | 24 +- atom/utils/core/argsview.hpp | 4 +- atom/utils/core/switch.hpp | 8 +- atom/utils/crypto/aes.hpp | 9 +- atom/utils/crypto/aes_impl.hpp | 4 +- atom/utils/debug/print.hpp | 51 +- atom/utils/format/difflib.cpp | 17 +- atom/utils/format/difflib.hpp | 33 +- atom/utils/format/xml.cpp | 5 +- atom/utils/lcg.hpp | 3 +- atom/utils/random/random.cpp | 4 +- atom/utils/random/uuid.hpp | 8 +- atom/utils/text/cstring.hpp | 8 +- atom/utils/text/string.cpp | 58 +- atom/utils/text/string.hpp | 47 +- atom/utils/text/valid_string.cpp | 19 +- atom/utils/text/valid_string.hpp | 14 +- atom/utils/time/qdatetime.hpp | 12 +- atom/utils/time/time.cpp | 11 +- atom/utils/time/time.hpp | 8 +- atom/web/CMakeLists.txt | 56 +- atom/web/address/CMakeLists.txt | 36 +- atom/web/address/address.cpp | 1 - atom/web/address/ipv4.cpp | 7 +- atom/web/address/ipv4.hpp | 10 +- atom/web/address/ipv6.cpp | 4 +- atom/web/address/ipv6.hpp | 10 +- atom/web/address/main.hpp | 4 +- atom/web/address/unix_domain.cpp | 4 +- atom/web/address/unix_domain.hpp | 9 +- atom/web/http/curl.cpp | 30 +- atom/web/http/downloader.hpp | 36 +- atom/web/mime/minetype.cpp | 2 +- atom/web/time/time_error.hpp | 1 - atom/web/time/time_manager_impl.hpp | 4 +- atom/web/utils/addr_info.cpp | 6 +- atom/web/utils/addr_info.hpp | 4 +- atom/web/utils/dns.cpp | 4 +- atom/web/utils/ip.cpp | 4 +- atom/web/utils/ip.hpp | 4 +- atom/web/utils/network.cpp | 1 - atom/web/utils/port.cpp | 7 +- atom/web/utils/socket.cpp | 6 +- atom/web/utils/socket.hpp | 4 +- cmake/FindGMock.cmake | 45 +- cmake/FindReadline.cmake | 285 +++--- cmake/FindYamlCpp.cmake | 129 ++- cmake/ModularInstall.cmake | 702 +++++++------- cmake/ModuleDependencies.cmake | 536 ++++++----- cmake/TestsBuildOptions.cmake | 13 +- cmake/UpdateBaseline.cmake | 153 +-- cmake/VersionConfig.cmake | 34 +- cmake/WindowsCompat.hpp | 16 +- cmake/module_dependencies.cmake | 16 +- cmake/version.h.in | 2 +- conanfile.py | 12 +- example/CMakeLists.txt | 115 ++- example/algorithm/CMakeLists.txt | 88 +- example/algorithm/graphics/flood.cpp | 76 +- example/async/CMakeLists.txt | 30 +- example/async/component_integration.cpp | 10 +- example/async/limiter.cpp | 14 +- example/async/timer.cpp | 37 +- .../components/advanced_bindings_example.cpp | 59 +- .../components/command_dispatch_example.cpp | 86 +- example/components/component_pool_example.cpp | 22 +- .../component_registry_advanced.cpp | 38 +- .../comprehensive_integration_example.cpp | 140 ++- .../components/game_entity_system_example.cpp | 130 ++- example/components/hot_reload_example.cpp | 74 +- .../iteration_optimization_example.cpp | 18 +- .../lifecycle_management_example.cpp | 83 +- example/components/lua_scripting_example.cpp | 4 +- .../performance_optimization_example.cpp | 58 +- .../plugin_architecture_example.cpp | 147 ++- .../components/python_scripting_example.cpp | 16 +- example/components/registry_example.cpp | 264 +++-- example/components/script_sandbox_example.cpp | 45 +- example/components/serialization_example.cpp | 38 +- .../components/type_conversion_example.cpp | 149 +-- .../components/unified_scripting_example.cpp | 44 +- .../variable_management_example.cpp | 95 +- example/connection/async_sockethub.cpp | 49 +- example/connection/async_tcpclient.cpp | 324 ++++--- example/connection/async_udpclient.cpp | 233 +++-- example/connection/async_udpserver.cpp | 187 ++-- example/connection/sockethub.cpp | 185 ++-- example/connection/sshclient.cpp | 88 +- example/connection/tcpclient.cpp | 314 +++--- example/connection/test_examples.py | 10 +- example/connection/udpclient.cpp | 287 +++--- example/connection/udpserver.cpp | 221 +++-- example/containers/CMakeLists.txt | 40 +- example/error/CMakeLists.txt | 32 +- example/extra/CMakeLists.txt | 70 +- example/extra/asio/mqtt_client.cpp | 38 +- example/extra/asio/sse_client.cpp | 53 +- example/extra/asio/sse_server.cpp | 51 +- example/extra/beast/http.cpp | 10 +- example/extra/beast/ws.cpp | 85 +- example/extra/curl/cookie.cpp | 78 +- example/extra/curl/multipart.cpp | 60 +- example/extra/curl/rest_client.cpp | 71 +- example/extra/curl/session.cpp | 74 +- example/extra/curl/websocket.cpp | 10 +- example/extra/dotenv/validation.cpp | 10 +- example/extra/iconv/advanced_features.cpp | 13 +- example/extra/iconv/basic_conversion.cpp | 91 +- example/extra/inicpp/advanced_features.cpp | 14 +- example/extra/inicpp/basic_usage.cpp | 49 +- example/extra/pugixml/advanced_features.cpp | 15 +- example/extra/pugixml/basic_usage.cpp | 61 +- example/extra/spdlog/advanced_features.cpp | 74 +- example/extra/spdlog/basic_usage.cpp | 43 +- example/extra/uv/coroutines.cpp | 3 +- example/extra/uv/message_bus.cpp | 84 +- example/image/CMakeLists.txt | 109 ++- example/image/README_NEW_EXAMPLES.md | 51 + example/image/core/CMakeLists.txt | 63 +- example/image/core/advanced_caching_demo.cpp | 523 +++++----- .../image/core/advanced_memory_management.cpp | 223 +++-- example/image/core/backend_integration.cpp | 4 +- example/image/core/basic_blob_operations.cpp | 2 +- example/image/core/blob_edge_cases.cpp | 89 +- .../image/core/boundary_conditions_demo.cpp | 467 ++++----- .../core/comprehensive_error_handling.cpp | 539 ++++++----- example/image/core/fast_blob_performance.cpp | 84 +- example/image/core/memory_management.cpp | 53 +- .../image/core/serialization_edge_cases.cpp | 260 ++--- example/image/formats/CMakeLists.txt | 159 +-- example/image/formats/advanced_formats.cpp | 207 ++-- .../image/formats/basic_ser_operations.cpp | 249 +++-- .../formats/format_conversion_edge_cases.cpp | 190 ++-- example/image/formats/medical_formats.cpp | 47 +- example/image/formats/raw_processing.cpp | 28 +- example/image/formats/scientific_formats.cpp | 74 +- .../image/formats/ser_quality_assessment.cpp | 152 +-- .../formats/ser_registration_stacking.cpp | 325 ++++--- example/image/formats/ser_video.cpp | 106 +- example/image/io/advanced_io.cpp | 43 +- example/image/io/basic_file_operations.cpp | 77 +- example/image/io/caching_prefetching.cpp | 75 +- example/image/io/format_conversion.cpp | 136 ++- example/image/io/memory_mapped_io.cpp | 572 ++++++----- example/image/io/streaming_io_demo.cpp | 550 ++++++----- example/image/metadata/CMakeLists.txt | 77 +- example/image/metadata/batch_metadata.cpp | 144 +-- example/image/metadata/exif_operations.cpp | 78 +- .../image/metadata/metadata_preservation.cpp | 230 ++--- example/image/processing/CMakeLists.txt | 172 ++-- example/image/processing/basic_filters.cpp | 12 +- .../processing/batch_processing_demo.cpp | 590 +++++++----- .../processing/computer_vision_advanced.cpp | 672 +++++++------ example/image/processing/gpu_acceleration.cpp | 67 +- .../processing/gpu_acceleration_demo.cpp | 399 ++++---- .../image/processing/image_enhancement.cpp | 12 +- example/image/processing/ml_processing.cpp | 163 ++-- .../image/processing/ml_processing_demo.cpp | 534 ++++++----- .../performance_optimization_demo.cpp | 906 ++++++++++-------- .../image/processing/realtime_processing.cpp | 525 +++++----- example/io/CMakeLists.txt | 142 +-- example/io/validate_examples.py | 2 +- example/memory/CMakeLists.txt | 53 +- example/meta/CMakeLists.txt | 57 +- example/meta/container_traits.cpp | 1 - example/meta/facade.cpp | 1 - example/meta/facade_any.cpp | 2 - example/meta/refl_json.cpp | 1 - example/meta/refl_yaml.cpp | 1 - example/meta/time.cpp | 1 - example/run_tests.py | 4 +- example/search/CMakeLists.txt | 78 +- example/secret/CMakeLists.txt | 39 +- example/sysinfo/CMakeLists.txt | 39 +- example/type/CMakeLists.txt | 32 +- example/utils/CMakeLists.txt | 32 +- .../mime/minetype_comprehensive_example.cpp | 178 ++-- ports/atom/portfile.cmake | 96 +- python/CMakeLists.txt | 2 +- python/algorithm/annealing.cpp | 2 +- python/algorithm/bignumber.cpp | 150 +-- python/algorithm/convolve.cpp | 2 +- python/algorithm/flood.cpp | 9 +- python/algorithm/flood_fill.cpp | 326 ++++--- python/algorithm/fraction.cpp | 132 ++- python/algorithm/gpu_math.cpp | 237 ++--- python/algorithm/image_ops.cpp | 779 ++++++++------- python/algorithm/math.cpp | 418 ++++---- python/algorithm/matrix.cpp | 491 ++++++---- python/algorithm/perlin.cpp | 191 ++-- python/algorithm/rust_numeric.cpp | 12 +- python/algorithm/simplex.cpp | 300 +++--- python/algorithm/uuid.cpp | 132 +-- python/async/async.cpp | 44 +- python/async/async_executor.cpp | 342 ++++--- python/async/daemon.cpp | 149 +-- python/async/eventstack.cpp | 243 ++--- python/async/future.cpp | 31 +- python/async/generator.cpp | 222 ++--- python/async/limiter.cpp | 219 +++-- python/async/lock.cpp | 28 +- python/async/message_queue.cpp | 341 +++---- python/async/packaged_task.cpp | 261 ++--- python/async/parallel.cpp | 256 ++--- python/async/pool.cpp | 325 ++++--- python/async/promise.cpp | 10 +- python/async/queue.cpp | 72 +- python/async/safetype.cpp | 274 +++--- python/async/threadlocal.cpp | 501 +++++----- python/async/timer.cpp | 199 ++-- python/connection/__init__.py | 58 +- python/connection/fifo.cpp | 1 - python/connection/sockethub.cpp | 1 - python/connection/sshclient.cpp | 51 +- python/connection/sync_fifoserver.cpp | 147 +-- python/connection/sync_tcpclient.cpp | 280 +++--- python/connection/ttybase.cpp | 230 +++-- python/connection/udpserver.cpp | 1 - python/connection/udpsockethub.cpp | 26 +- python/error/error_code.cpp | 550 +++++++---- python/error/error_context.cpp | 201 ++-- python/error/error_formatter.cpp | 172 ++-- python/error/error_handler.cpp | 76 +- python/error/error_recovery.cpp | 108 ++- python/error/exception.cpp | 135 ++- python/error/stacktrace.cpp | 155 +-- python/extra/asio/mqtt.cpp | 207 ++-- python/extra/beast/http.cpp | 1 - python/extra/boost/locale.cpp | 7 +- python/extra/boost/math.cpp | 21 +- python/extra/boost/system.cpp | 5 +- python/extra/boost/uuid.cpp | 1 - python/extra/curl/session.cpp | 91 +- python/extra/dotenv/dotenv.cpp | 95 +- python/extra/iconv/iconv.cpp | 1 - python/extra/inicpp/inicpp.cpp | 138 +-- python/extra/injection/injection.cpp | 159 +-- python/extra/pugixml/pugixml.cpp | 116 ++- python/extra/spdlog/spdlog.cpp | 257 +++-- python/extra/uv/uv.cpp | 253 +++-- python/io/core_glob.cpp | 449 +++++---- python/io/core_io.cpp | 90 +- python/io/file_info.cpp | 222 +++-- python/io/file_permission.cpp | 337 ++++--- python/search/__init__.py | 2 +- python/search/cache.cpp | 36 +- python/search/mysql.cpp | 24 +- python/search/sqlite.cpp | 4 +- python/search/ttl.cpp | 51 +- python/sysinfo/API_COVERAGE_VERIFICATION.md | 29 +- python/sysinfo/COMPILATION_CHECKLIST.md | 30 +- python/sysinfo/battery.cpp | 24 +- python/sysinfo/gpu.cpp | 14 +- python/sysinfo/locale.cpp | 27 +- python/sysinfo/sn.cpp | 113 ++- python/sysinfo/test_bindings.py | 98 +- python/sysinfo/virtual.cpp | 15 +- python/sysinfo/wm.cpp | 108 +-- python/system/clipboard.cpp | 144 +-- python/system/command.cpp | 8 +- python/system/crash_quotes.cpp | 1 - python/system/crontab.cpp | 51 +- python/system/device.cpp | 36 +- python/system/gpio.cpp | 20 +- python/system/network_manager.cpp | 130 +-- python/system/process_info.cpp | 18 +- python/system/shortcut.cpp | 128 +-- python/system/signal.cpp | 20 +- python/system/software.cpp | 168 ++-- python/system/storage.cpp | 1 - python/system/virtual_network.cpp | 139 +-- python/type/robin_hood.cpp | 6 +- python/utils/aligned.cpp | 14 +- python/utils/argsview.cpp | 292 +++--- python/utils/bit.cpp | 106 +- python/utils/container.cpp | 330 ++++--- python/utils/conversion.cpp | 140 +-- python/utils/difflib.cpp | 406 ++++---- python/utils/qprocess.cpp | 1 - python/utils/switch.cpp | 266 ++--- python/utils/to_string.cpp | 341 +++---- python/utils/uuid.cpp | 4 +- python/utils/valid_string.cpp | 230 +++-- python/web/__init__.py | 67 +- python/web/curl.cpp | 79 +- python/web/downloader.cpp | 1 - python/web/mimetype.cpp | 5 +- python/web/time.cpp | 182 ++-- python/web/utils.cpp | 12 +- scripts/README.md | 11 + scripts/SCRIPT_ORGANIZATION_BACKUP.md | 7 + scripts/build-and-package.py | 14 +- scripts/create-portable.py | 10 +- scripts/disable-gtest-discovery.ps1 | 1 - scripts/modular-installer.py | 10 +- scripts/test_examples.ps1 | 29 +- scripts/test_examples_simple.ps1 | 19 +- scripts/validate-build-system.py | 95 +- scripts/validate-package.py | 14 +- setup.py | 11 +- tests/CMakeLists.txt | 4 +- tests/algorithm/CMakeLists.txt | 226 ++--- .../compression/test_compression.cpp | 17 +- tests/algorithm/core/test_algorithm.cpp | 6 - tests/algorithm/hash/test_md5.cpp | 28 +- tests/algorithm/math/test_bignumber.cpp | 4 +- tests/algorithm/math/test_matrix.cpp | 2 +- tests/algorithm/signal/test_convolve.cpp | 25 +- tests/algorithm/utils/test_flood.cpp | 142 +-- tests/apply_template.py | 63 +- tests/async/core/test_future.cpp | 1 - tests/async/core/test_queue.cpp | 2 +- tests/async/execution/test_async_executor.cpp | 121 ++- tests/async/sync/test_limiter.cpp | 92 +- tests/async/sync/test_safetype.cpp | 210 ++-- tests/async/sync/test_slot.cpp | 93 +- tests/async/sync/test_trigger.cpp | 41 +- tests/async/test_fixtures.hpp | 104 +- tests/async/test_utils.hpp | 157 ++- tests/async/threading/test_lock.cpp | 108 ++- tests/async/threading/test_threadlocal.cpp | 94 +- tests/async/utils/test_generator.cpp | 43 +- tests/async/utils/test_timer.cpp | 156 ++- tests/components/CMakeLists.txt | 156 +-- tests/components/README.md | 24 + tests/components/advanced_bindings.cpp | 57 +- tests/components/component.cpp | 105 +- tests/components/component_pool.cpp | 73 +- tests/components/dispatch.cpp | 30 +- tests/components/iteration.cpp | 66 +- tests/components/lifecycle.cpp | 187 ++-- tests/components/registry.cpp | 59 +- tests/components/script_sandbox.cpp | 11 +- tests/components/scripting_api.cpp | 55 +- tests/components/serialization.cpp | 34 +- tests/components/type_conversion.cpp | 69 +- tests/components/var.cpp | 19 +- tests/connection/CMakeLists.txt | 115 ++- .../connection/fifo/test_async_fifoclient.cpp | 8 +- .../connection/fifo/test_async_fifoserver.cpp | 21 +- tests/connection/fifo/test_fifoclient.cpp | 42 +- tests/connection/fifo/test_fifoserver.cpp | 20 +- .../socket/test_async_sockethub.cpp | 44 +- tests/connection/socket/test_sockethub.cpp | 4 +- tests/connection/ssh/test_sshserver.cpp | 49 +- tests/connection/tcp/test_async_tcpclient.cpp | 42 +- tests/connection/tcp/test_tcpclient.cpp | 49 +- tests/connection/tty/test_ttybase.cpp | 27 +- tests/connection/udp/test_async_udpclient.cpp | 54 +- tests/connection/udp/test_async_udpserver.cpp | 45 +- tests/connection/udp/test_udpclient.cpp | 4 +- tests/connection/udp/test_udpserver.cpp | 71 +- tests/containers/test_containers.cpp | 121 ++- tests/error/test_error_formatting.cpp | 176 ++-- tests/error/test_error_integration.cpp | 224 +++-- tests/extra/asio/test_asio_compatibility.cpp | 6 +- tests/extra/asio/test_mqtt_client.cpp | 182 ++-- tests/extra/asio/test_sse.cpp | 10 +- tests/extra/beast/test_http.cpp | 101 +- tests/extra/beast/test_ws.cpp | 58 +- tests/extra/boost/test_charconv.hpp | 1 - tests/extra/boost/test_locale.hpp | 1 - tests/extra/curl/test_rest_client.hpp | 1 - tests/extra/dotenv/test_dotenv.cpp | 32 +- tests/extra/inicpp/common.cpp | 3 +- tests/extra/inicpp/file.cpp | 2 +- tests/image/CMakeLists.txt | 576 +++++------ tests/image/TEST_SUITE_SUMMARY.md | 27 + tests/image/test_advanced_formats.hpp | 139 +-- tests/image/test_computer_vision.hpp | 143 +-- tests/image/test_enhancement.hpp | 251 ++--- tests/image/test_exif.hpp | 151 ++- tests/image/test_filters.hpp | 52 +- tests/image/test_fits_data.hpp | 138 ++- tests/image/test_fits_file.hpp | 78 +- tests/image/test_fits_header.hpp | 36 +- tests/image/test_fits_utils.hpp | 98 +- tests/image/test_format_detector.hpp | 146 +-- tests/image/test_gpu_acceleration.hpp | 61 +- tests/image/test_image_blob.hpp | 95 +- tests/image/test_image_loader.hpp | 152 +-- tests/image/test_image_processor.hpp | 328 ++++--- tests/image/test_image_saver.hpp | 118 +-- tests/image/test_ml_processing.hpp | 277 +++--- tests/image/test_ocr.hpp | 74 +- tests/image/test_performance.hpp | 289 +++--- tests/image/test_realtime.hpp | 111 +-- tests/image/test_runner.cpp | 263 ++--- tests/image/test_ser.hpp | 70 +- tests/image/test_transforms.hpp | 256 ++--- tests/image/test_utils.hpp | 104 +- tests/image/validate_tests.py | 105 +- tests/io/CMakeLists.txt | 127 ++- tests/io/async/test_async_compress.cpp | 155 +-- tests/io/async/test_async_glob.cpp | 35 +- tests/io/async/test_async_io.cpp | 229 ++--- tests/io/compression/test_compress.cpp | 49 +- tests/io/core/test_glob.cpp | 69 +- tests/io/core/test_io.cpp | 94 +- tests/io/core/test_io_main.cpp | 5 +- tests/io/filesystem/test_file_permission.cpp | 121 +-- tests/io/filesystem/test_pushd.cpp | 74 +- tests/log/CMakeLists.txt | 41 +- tests/log/test_logger.cpp | 37 +- tests/memory/CMakeLists.txt | 146 ++- tests/memory/test_memory.cpp | 35 +- tests/memory/test_memory_pool.cpp | 51 +- tests/memory/test_object.cpp | 21 +- tests/memory/test_ring.cpp | 32 +- tests/memory/test_tracker.cpp | 14 +- tests/meta/CMakeLists.txt | 161 ++-- tests/meta/core/test_any.hpp | 13 +- tests/meta/core/test_anymeta.hpp | 257 +++-- tests/meta/core/test_container_traits.hpp | 184 ++-- tests/meta/interop/test_ffi.cpp | 9 +- tests/meta/proxy/test_facade.hpp | 49 +- tests/meta/reflection/test_refl.hpp | 206 ++-- tests/meta/utils/test_decorate.cpp | 31 +- tests/meta/utils/test_enum.hpp | 88 +- tests/meta/utils/test_property.hpp | 3 +- tests/search/CMakeLists.txt | 93 +- tests/search/IMPLEMENTATION_SUMMARY.md | 21 + tests/search/TEST_COVERAGE_PLAN.md | 18 + tests/search/main.cpp | 2 +- tests/search/test_error_handling.hpp | 117 ++- tests/search/test_lru.hpp | 23 +- tests/search/test_mysql.hpp | 233 ++--- tests/search/test_search.hpp | 49 +- tests/search/test_search_engine.hpp | 231 +++-- tests/secret/test_secret.cpp | 55 +- tests/serial/CMakeLists.txt | 99 +- tests/serial/test_serial.cpp | 127 +-- tests/serial/test_serial_port.hpp | 127 ++- tests/sysinfo/CMakeLists.txt | 378 ++++---- tests/sysinfo/battery.cpp | 127 ++- tests/sysinfo/bios.cpp | 2 +- tests/sysinfo/cpu.cpp | 59 +- tests/sysinfo/gpu.cpp | 55 +- tests/sysinfo/locale.cpp | 76 +- tests/sysinfo/os.cpp | 21 +- tests/sysinfo/sn.cpp | 44 +- tests/sysinfo/sysinfo_printer.cpp | 86 +- tests/sysinfo/test_sysinfo.cpp | 266 ++--- tests/sysinfo/virtual.cpp | 43 +- tests/sysinfo/wifi.cpp | 16 +- tests/sysinfo/wm.cpp | 134 +-- tests/system/CMakeLists.txt | 99 +- tests/system/test_command.cpp | 24 +- tests/system/test_device.cpp | 121 ++- tests/system/test_gpio.cpp | 130 +-- tests/system/test_lregistry.hpp | 89 +- tests/system/test_network_manager.cpp | 93 +- tests/system/test_network_manager.hpp | 45 +- tests/system/test_priority.cpp | 138 +-- tests/system/test_process_manager.cpp | 131 +-- tests/system/test_stat.cpp | 36 +- tests/system/test_storage.cpp | 46 +- tests/system/test_voltage.cpp | 36 +- tests/tests/test_common.hpp | 102 +- tests/type/CMakeLists.txt | 128 ++- tests/type/TEST_COVERAGE_REPORT.md | 28 +- tests/type/test_args.cpp | 23 +- tests/type/test_argsview.hpp | 19 +- tests/type/test_compat.hpp | 36 +- tests/type/test_indestructible.hpp | 4 +- tests/type/test_json-schema.hpp | 1 - tests/type/test_no_offset_ptr.hpp | 1 - tests/type/test_noncopyable.hpp | 22 +- tests/type/test_pod_vector.cpp | 2 +- tests/type/test_qvariant.cpp | 41 +- tests/type/test_rjson.cpp | 39 +- tests/type/test_robin_hood.hpp | 45 +- tests/type/test_rtype.hpp | 45 +- tests/type/test_small_list.hpp | 11 +- tests/type/test_static_string.hpp | 26 +- tests/type/test_static_vector.hpp | 1 - tests/type/test_uint.cpp | 2 +- tests/utils/container/test_ranges.hpp | 186 ++-- tests/utils/container/test_span.hpp | 94 +- tests/utils/conversion/test_convert.hpp | 66 +- tests/utils/conversion/test_to_byte.hpp | 127 ++- tests/utils/core/test_bit.hpp | 120 +-- tests/utils/core/test_runner.cpp | 30 +- tests/utils/core/test_to_any.hpp | 3 +- tests/utils/crypto/test_aes.hpp | 111 ++- tests/utils/crypto/test_aes_main.cpp | 10 +- tests/utils/debug/test_color_print.hpp | 141 +-- tests/utils/debug/test_print.hpp | 20 +- tests/utils/format/test_difflib.hpp | 134 +-- tests/utils/math/test_uuid.hpp | 116 ++- tests/utils/memory/test_aligned.hpp | 195 ++-- tests/utils/memory/test_simd_wrapper.hpp | 163 ++-- tests/utils/string/test_cstring.hpp | 5 +- tests/utils/string/test_string.hpp | 38 +- tests/utils/string/test_utf.hpp | 125 +-- tests/utils/system/test_argsview.hpp | 2 +- tests/web/CMakeLists.txt | 138 ++- tests/web/address/test_address.hpp | 357 +++---- tests/web/http/test_curl.hpp | 40 +- tests/web/integration/test_integration.hpp | 262 +++-- tests/web/time/test_time.hpp | 53 +- tests/web/utils/test_addr_info.hpp | 48 +- tests/web/utils/test_dns.hpp | 10 +- tests/web/utils/test_ip.hpp | 231 +++-- tests/web/utils/test_network.hpp | 93 +- tests/web/utils/test_port.hpp | 123 +-- tests/web/utils/test_socket.hpp | 167 ++-- 843 files changed, 38340 insertions(+), 31639 deletions(-) diff --git a/.augment/rules/build-examples-fix.md b/.augment/rules/build-examples-fix.md index 1be2daa5..721f734c 100644 --- a/.augment/rules/build-examples-fix.md +++ b/.augment/rules/build-examples-fix.md @@ -12,6 +12,7 @@ Build all examples in the project completely and fix any issues encountered duri 6. Document any changes made to fix build issues Please provide a summary of: + - Which examples were built - What issues were encountered and how they were resolved - Verification that all functionality is working properly diff --git a/.augment/rules/file-and-code-management.md b/.augment/rules/file-and-code-management.md index 2151ec3f..0947802f 100644 --- a/.augment/rules/file-and-code-management.md +++ b/.augment/rules/file-and-code-management.md @@ -3,18 +3,22 @@ type: "always_apply" --- # FILE AND CODE MANAGEMENT PROTOCOLS + ## STRICT RULES FOR FILE OPERATIONS AND CODE CHANGES ### FILE SIZE AND ORGANIZATION MANDATE #### Rule 1: Reasonable File Size Management + - You MUST keep files at reasonable sizes for good workspace organization - Large files SHOULD be split into multiple logical files for ease of use - You MUST verify file sizes using `wc -c filename` when working with large content - If a file becomes unwieldy, you MUST suggest splitting it into multiple files #### Rule 2: File Organization Best Practices + **MANDATORY APPROACH for file management:** + 1. Calculate planned content size for new files 2. If creating large content: consider logical file splitting 3. For existing files: check current size with `wc -c filename` @@ -22,7 +26,9 @@ type: "always_apply" 5. Maintain logical organization and clear file purposes #### Rule 3: Size Monitoring and Reporting + **MANDATORY SEQUENCE for large file operations:** + 1. `wc -c filename` to check current file size 2. Report file size when working with substantial content 3. Suggest file splitting when content becomes unwieldy @@ -30,8 +36,10 @@ type: "always_apply" ### FILE CREATION PROTOCOLS -#### New File Creation Requirements: +#### New File Creation Requirements + **MANDATORY SEQUENCE - NO DEVIATIONS:** + 1. `view` directory to confirm file doesn't exist 2. `codebase-retrieval` to understand project structure and conventions 3. Calculate character count of planned content @@ -45,7 +53,8 @@ type: "always_apply" **SKIPPING ANY STEP = IMMEDIATE TASK TERMINATION** -#### File Creation Reporting Format: +#### File Creation Reporting Format + ``` FILE CREATION REPORT: FILENAME: [exact filename] @@ -60,8 +69,10 @@ COMPLIANCE STATUS: [COMPLIANT/VIOLATION] ### FILE MODIFICATION PROTOCOLS -#### Existing File Modification Requirements: +#### Existing File Modification Requirements + **MANDATORY SEQUENCE - NO DEVIATIONS:** + 1. `view` file to examine current contents and structure 2. `wc -c filename` to get current size 3. `codebase-retrieval` to understand context and dependencies @@ -77,7 +88,8 @@ COMPLIANCE STATUS: [COMPLIANT/VIOLATION] **SKIPPING ANY STEP = IMMEDIATE TASK TERMINATION** -#### File Modification Reporting Format: +#### File Modification Reporting Format + ``` FILE MODIFICATION REPORT: FILENAME: [exact filename] @@ -95,8 +107,10 @@ ERROR CHECK: [diagnostics results] ### CODE CHANGE MANAGEMENT -#### Pre-Change Requirements: +#### Pre-Change Requirements + **MANDATORY VERIFICATION CHAIN:** + 1. `codebase-retrieval` - understand current implementation thoroughly 2. `view` - examine ALL files that will be modified 3. `diagnostics` - establish baseline error state @@ -105,15 +119,18 @@ ERROR CHECK: [diagnostics results] 6. Verify all dependencies and imports exist 7. Confirm no breaking changes to existing functionality -#### Change Implementation Rules: +#### Change Implementation Rules + - You MUST use `str-replace-editor` for ALL existing file modifications - You are FORBIDDEN from using `save-file` to overwrite existing files - You MUST specify exact line numbers for all replacements - You MUST ensure `old_str` matches EXACTLY (including whitespace) - You MUST make changes in logical, atomic units -#### Post-Change Requirements: +#### Post-Change Requirements + **MANDATORY VERIFICATION CHAIN:** + 1. `diagnostics` - verify no new errors introduced 2. `wc -c` - verify all modified files comply with size limits 3. `view` - spot-check critical changes were applied correctly @@ -122,14 +139,17 @@ ERROR CHECK: [diagnostics results] ### TESTING REQUIREMENTS -#### Mandatory Testing Protocol: +#### Mandatory Testing Protocol + **You MUST test changes when:** + - Any code functionality is modified - New files with executable code are created - Configuration files are changed - Dependencies are modified -#### Testing Sequence: +#### Testing Sequence + 1. `diagnostics` - check for syntax/compilation errors 2. `launch-process` - run unit tests if they exist 3. `launch-process` - run integration tests if they exist @@ -137,8 +157,10 @@ ERROR CHECK: [diagnostics results] 5. `read-process` - capture and analyze all test outputs 6. Report test results with exact output details -#### Test Failure Protocol: +#### Test Failure Protocol + When tests fail: + 1. **IMMEDIATELY** stop further changes 2. **REPORT** exact test failure details 3. **ANALYZE** failure using `diagnostics` @@ -148,8 +170,10 @@ When tests fail: ### ROLLBACK PROCEDURES -#### When Changes Fail: +#### When Changes Fail + **MANDATORY ROLLBACK SEQUENCE:** + 1. **IMMEDIATELY** stop making further changes 2. **DOCUMENT** exactly what was changed and what failed 3. **USE** `str-replace-editor` to revert changes in reverse order @@ -158,7 +182,8 @@ When tests fail: 6. **PRESENT** failure analysis to user 7. **AWAIT** user instructions for alternative approach -#### Rollback Verification: +#### Rollback Verification + - You MUST verify each rollback step using appropriate tools - You MUST confirm system returns to pre-change state - You MUST run tests to verify rollback success @@ -166,13 +191,15 @@ When tests fail: ### DEPENDENCY MANAGEMENT -#### Package Manager Mandate: +#### Package Manager Mandate + - You MUST use appropriate package managers for dependency changes - You are FORBIDDEN from manually editing package files (package.json, requirements.txt, etc.) - You MUST use: npm/yarn/pnpm for Node.js, pip/poetry for Python, cargo for Rust, etc. - **MANUAL PACKAGE FILE EDITING = IMMEDIATE TASK TERMINATION** -#### Dependency Change Protocol: +#### Dependency Change Protocol + 1. `view` current package configuration 2. `codebase-retrieval` to understand project dependencies 3. Present dependency change plan to user @@ -183,14 +210,16 @@ When tests fail: ### DOCUMENTATION REQUIREMENTS -#### You MUST Document: +#### You MUST Document + - Every file created with purpose and structure - Every modification made with rationale - Every test performed with results - Every failure encountered with analysis - Every rollback performed with verification -#### Documentation Format: +#### Documentation Format + ``` CHANGE DOCUMENTATION: TIMESTAMP: [when change was made] @@ -207,6 +236,7 @@ STATUS: [SUCCESS/FAILURE/ROLLED_BACK] ### QUALITY GATES #### Gate 1: Pre-Change Verification + - [ ] All information gathered and verified - [ ] User approval obtained - [ ] Size limits confirmed @@ -214,12 +244,14 @@ STATUS: [SUCCESS/FAILURE/ROLLED_BACK] - [ ] Test plan established #### Gate 2: Implementation Verification + - [ ] Changes made using correct tools - [ ] Size limits maintained - [ ] No syntax errors introduced - [ ] All modifications documented #### Gate 3: Post-Change Verification + - [ ] Tests pass or failures documented - [ ] Size compliance verified - [ ] No new errors introduced diff --git a/.augment/rules/information-verification-chains.md b/.augment/rules/information-verification-chains.md index 6b477516..8fd489e1 100644 --- a/.augment/rules/information-verification-chains.md +++ b/.augment/rules/information-verification-chains.md @@ -3,14 +3,17 @@ type: "always_apply" --- # INFORMATION VERIFICATION CHAINS + ## ANTI-GUESSING PROTOCOLS WITH MANDATORY VERIFICATION ### FUNDAMENTAL VERIFICATION PRINCIPLE + **YOU ARE FORBIDDEN FROM USING ANY INFORMATION THAT HAS NOT BEEN TOOL-VERIFIED** ### INFORMATION CLASSIFICATION -#### CRITICAL INFORMATION (Requires 2-Tool Verification): +#### CRITICAL INFORMATION (Requires 2-Tool Verification) + - File paths and locations - Function/method signatures - Class definitions and properties @@ -20,14 +23,16 @@ type: "always_apply" - User preferences - Error states and diagnostics -#### STANDARD INFORMATION (Requires 1-Tool Verification): +#### STANDARD INFORMATION (Requires 1-Tool Verification) + - File contents - Directory listings - Process outputs - Tool results - Documentation content -#### FORBIDDEN ASSUMPTIONS (Never Assume These): +#### FORBIDDEN ASSUMPTIONS (Never Assume These) + - File existence or location - Function parameter types or names - Import statements or dependencies @@ -39,7 +44,9 @@ type: "always_apply" ### MANDATORY VERIFICATION CHAINS #### Chain 1: File Information Verification + **REQUIRED SEQUENCE:** + 1. `view` directory to confirm file exists 2. `view` file to examine current contents 3. `codebase-retrieval` to understand context (if modifying) @@ -47,6 +54,7 @@ type: "always_apply" 5. Report verification status explicitly **EXAMPLE MANDATORY REPORTING:** + ``` VERIFICATION CHAIN: File Information TOOL 1: view - confirmed file exists at path X @@ -56,7 +64,9 @@ STATUS: VERIFIED - proceeding with confidence ``` #### Chain 2: Code Structure Verification + **REQUIRED SEQUENCE:** + 1. `codebase-retrieval` for broad structural understanding 2. `view` with `search_query_regex` for specific symbols 3. `diagnostics` to check current error state @@ -64,7 +74,9 @@ STATUS: VERIFIED - proceeding with confidence 5. Report any discrepancies immediately #### Chain 3: Project State Verification + **REQUIRED SEQUENCE:** + 1. `view` project root directory 2. `codebase-retrieval` for project overview 3. `diagnostics` for current issues @@ -73,14 +85,17 @@ STATUS: VERIFIED - proceeding with confidence ### INFORMATION FRESHNESS REQUIREMENTS -#### Freshness Rules: +#### Freshness Rules + - Information from current conversation: VALID - Information from previous conversations: INVALID (must re-verify) - Cached assumptions about project state: INVALID (must re-verify) - Tool results from current session: VALID until project changes -#### Re-verification Triggers: +#### Re-verification Triggers + You MUST re-verify information when: + - User mentions any changes were made - Any file modification occurs - Any error state changes @@ -89,14 +104,16 @@ You MUST re-verify information when: ### UNCERTAINTY MANAGEMENT PROTOCOL -#### When You Encounter Uncertainty: +#### When You Encounter Uncertainty + 1. **IMMEDIATELY** stop current task 2. **EXPLICITLY** state: "UNCERTAINTY DETECTED: [specific uncertainty]" 3. **LIST** exactly what information you need 4. **PROPOSE** specific tools to gather missing information 5. **WAIT** for user approval before proceeding -#### Uncertainty Reporting Format: +#### Uncertainty Reporting Format + ``` UNCERTAINTY DETECTED: [specific thing you're uncertain about] MISSING INFORMATION: [exactly what you need to know] @@ -107,8 +124,10 @@ RECOMMENDATION: [wait for verification vs. ask user for guidance] ### CROSS-VALIDATION REQUIREMENTS -#### For Critical Decisions: +#### For Critical Decisions + You MUST verify using TWO different tools and report: + ``` CROSS-VALIDATION REPORT: PRIMARY TOOL: [tool name] - [result] @@ -118,8 +137,10 @@ CONFIDENCE LEVEL: [HIGH/MEDIUM/LOW based on agreement] PROCEEDING: [YES/NO with justification] ``` -#### Conflict Resolution Protocol: +#### Conflict Resolution Protocol + When tools provide conflicting information: + 1. **IMMEDIATELY** report the conflict 2. **DO NOT** choose which tool to believe 3. **PRESENT** both results to user @@ -128,14 +149,16 @@ When tools provide conflicting information: ### INFORMATION AUDIT TRAIL -#### You MUST Maintain Record Of: +#### You MUST Maintain Record Of + - Every piece of information you use - Which tool provided each piece of information - When the information was gathered - How the information was verified - Any assumptions you made (FORBIDDEN - but if detected, must report) -#### Audit Trail Format: +#### Audit Trail Format + ``` INFORMATION AUDIT TRAIL: TIMESTAMP: [when gathered] @@ -148,14 +171,16 @@ USAGE: [how you used this information] ### VERIFICATION FAILURE PROTOCOLS -#### When Verification Fails: +#### When Verification Fails + 1. **IMMEDIATELY** stop using the unverified information 2. **REPORT** verification failure with details 3. **IDENTIFY** alternative verification methods 4. **REQUEST** user guidance on how to proceed 5. **DO NOT** proceed with unverified information -#### When Tools Disagree: +#### When Tools Disagree + 1. **IMMEDIATELY** report disagreement 2. **PRESENT** all conflicting information 3. **DO NOT** make judgment calls about which is correct @@ -164,7 +189,8 @@ USAGE: [how you used this information] ### MANDATORY PRE-ACTION VERIFICATION -#### Before ANY Action, You MUST Verify: +#### Before ANY Action, You MUST Verify + - [ ] All file paths exist and are accessible - [ ] All functions/methods exist with correct signatures - [ ] All dependencies are available @@ -173,8 +199,10 @@ USAGE: [how you used this information] - [ ] User has approved the planned action - [ ] All tools needed are available and working -#### Verification Checklist Reporting: +#### Verification Checklist Reporting + You MUST report completion of this checklist: + ``` PRE-ACTION VERIFICATION COMPLETE: ✓ File paths verified via [tool] @@ -190,16 +218,19 @@ STATUS: CLEARED FOR ACTION ### INFORMATION QUALITY GATES #### Quality Gate 1: Source Verification + - Information MUST come from tool output - Information MUST be current (from this conversation) - Information MUST be complete (no partial assumptions) #### Quality Gate 2: Cross-Validation + - Critical information MUST be verified by 2+ tools - Conflicting information MUST be escalated - Uncertain information MUST be flagged #### Quality Gate 3: User Confirmation + - Significant actions MUST have user approval - Assumptions MUST be confirmed with user - Uncertainties MUST be disclosed to user diff --git a/.augment/rules/update-examples.md b/.augment/rules/update-examples.md index 8aa667fb..bdf37793 100644 --- a/.augment/rules/update-examples.md +++ b/.augment/rules/update-examples.md @@ -13,6 +13,7 @@ I will provide you with two folders: an implementation folder containing the sou - Ensuring all branches and conditional logic are exampleed Requirements: + - Use the same exampleing framework and patterns as the existing examples - Maintain consistency with existing example naming conventions and structure - Ensure all new examples are properly documented with clear example descriptions diff --git a/.augment/rules/update-python.md b/.augment/rules/update-python.md index 9606c91d..2ef1ca2d 100644 --- a/.augment/rules/update-python.md +++ b/.augment/rules/update-python.md @@ -20,4 +20,4 @@ I will provide you with two folders shortly. I need you to systematically update 5. **Consistency**: Ensure naming conventions and documentation style are consistent across all binding files 6. **Error Handling**: Properly handle C++ exceptions and convert them to appropriate Python exceptions -Please work through each module systematically, and let me know when you've completed each one so I can review the changes before proceeding to the next module. \ No newline at end of file +Please work through each module systematically, and let me know when you've completed each one so I can review the changes before proceeding to the next module. diff --git a/.augment/rules/update-tests.md b/.augment/rules/update-tests.md index 2d0dc17f..9a2134e1 100644 --- a/.augment/rules/update-tests.md +++ b/.augment/rules/update-tests.md @@ -13,6 +13,7 @@ I will provide you with two folders: an implementation folder containing the sou - Ensuring all branches and conditional logic are tested Requirements: + - Use the same testing framework and patterns as the existing tests - Maintain consistency with existing test naming conventions and structure - Ensure all new tests are properly documented with clear test descriptions diff --git a/AGENTS.md b/AGENTS.md index 4901f1f4..4bc75f9c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,7 @@ # Repository Guidelines ## Project Structure & Module Organization + - `atom/` — C++ core library, organized by domain (algorithm, async, io, etc.). - `python/` — Pybind11 bindings and the `atom` Python package. - `tests/` — C++ test suite (GoogleTest via CMake/CTest). Python tests, if any, also live here. @@ -8,6 +9,7 @@ - `cmake/`, `scripts/`, `example/`, `build/` (generated). ## Build, Test, and Development Commands + - C++ build (Ninja default): `cmake --preset release && cmake --build --preset release -j` - C++ tests: `cmake --preset debug && cmake --build --preset debug -j && ctest --preset default --output-on-failure` - Cross‑platform scripts: `./build.sh` (Unix) or `build.bat` (Windows) - wrapper scripts for backward compatibility @@ -17,17 +19,21 @@ - Docs: Sphinx `sphinx-build -b html docs docs/_build`; Doxygen `doxygen Doxyfile` ## Coding Style & Naming Conventions + - C++: 4‑space indent, 80‑column guide; format with `clang-format` (see `.clang-format`). - Naming (C++): camelCase for variables/functions, PascalCase for classes/namespaces, UPPER_SNAKE_CASE for constants, files `lower_snake_case.[cpp|hpp]` (see `STYLE_OF_CODE.md`). Prefer Doxygen comments. - Python: Black (88 cols), isort, Ruff, MyPy (configured in `pyproject.toml`). Run: `pre-commit run -a`. ## Testing Guidelines + - C++: Use GoogleTest; place tests under `tests//` and register targets in the local `CMakeLists.txt`. Run via CTest; include edge cases and failure paths. - Python: pytest patterns `test_*.py`, marks available (`unit`, `integration`, `slow`). Aim to keep coverage healthy; prefer small, focused tests. ## Commit & Pull Request Guidelines + - Commits: short imperative subject (≤72 chars), descriptive body when needed. Reference issues (`#123`). Conventional commit prefixes are optional. - PRs: clear description, rationale, linked issues, tests added/updated, and doc changes if behavior/user‑facing APIs change. Ensure `pre-commit` passes and CI is green. ## Security & Configuration Tips + - Don’t commit secrets; prefer env vars. Build requires CMake ≥3.21 and a modern compiler (MSVC 2022/GCC/Clang). C/C++ deps via vcpkg/Conan; Python ≥3.8. diff --git a/CLAUDE.md b/CLAUDE.md index 7ae929b2..07a11511 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -22,6 +22,7 @@ The project uses CMake as the primary build system with enhanced build scripts: ### Build System Features The enhanced build scripts support: + - Multiple build types (debug, release, relwithdebinfo) - Parallel compilation with automatic CPU detection - System dependency installation @@ -32,6 +33,7 @@ The enhanced build scripts support: ### Module-Based Building You can selectively build modules using CMake options: + - `ATOM_BUILD_ALGORITHM=ON/OFF` - Algorithm and mathematical operations - `ATOM_BUILD_IMAGE=ON/OFF` - Image processing and computer vision - `ATOM_BUILD_ASYNC=ON/OFF` - Asynchronous operations @@ -74,6 +76,7 @@ Atom is organized into modular components under `atom/`: ### Python Bindings Python bindings are available for most modules using pybind11: + - Enable with `--python` flag or `ATOM_BUILD_PYTHON_BINDINGS=ON` - Bindings are located in `python/` directory - Each module has corresponding Python binding files @@ -107,18 +110,21 @@ Atom uses a comprehensive error handling system centered in the `error` module. ## Common Development Tasks ### Building a Single Module + ```bash cmake -B build -DATOM_BUILD_ALGORITHM=ON -DATOM_BUILD_TESTS=ON cmake --build build ``` ### Running Specific Tests + ```bash cd build ctest -R "algorithm_*" --output-on-failure ``` ### Building with All Features + ```bash ./scripts/build.sh --release --python --examples --tests --docs --package -``` \ No newline at end of file +``` diff --git a/CMakeLists.txt b/CMakeLists.txt index f5934f93..0bc3766f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,12 +1,10 @@ -# CMakeLists.txt for Atom Project -# Licensed under GPL3 -# Author: Max Qian +# CMakeLists.txt for Atom Project Licensed under GPL3 Author: Max Qian cmake_minimum_required(VERSION 3.21) # Set a global policy to handle malformed package configurations if(POLICY CMP0000) - cmake_policy(SET CMP0000 NEW) + cmake_policy(SET CMP0000 NEW) endif() # Set minimum policy version to handle system package issues @@ -17,8 +15,7 @@ project( LANGUAGES C CXX VERSION 0.1.0 DESCRIPTION "Foundational library for astronomical software" - HOMEPAGE_URL "https://github.com/ElementAstro/Atom" -) + HOMEPAGE_URL "https://github.com/ElementAstro/Atom") # ----------------------------------------------------------------------------- # Include CMake Modules @@ -36,36 +33,39 @@ set(REQUIRED_CMAKE_MODULES TestsBuildOptions.cmake ScanModule.cmake PackagingConfig.cmake - ModularInstall.cmake -) + ModularInstall.cmake) foreach(MODULE ${REQUIRED_CMAKE_MODULES}) - set(MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/${MODULE}") - if(EXISTS "${MODULE_PATH}") - include(cmake/${MODULE}) - message(STATUS "Included CMake module: ${MODULE}") - else() - message(WARNING "CMake module not found: ${MODULE_PATH}") - endif() + set(MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/${MODULE}") + if(EXISTS "${MODULE_PATH}") + include(cmake/${MODULE}) + message(STATUS "Included CMake module: ${MODULE}") + else() + message(WARNING "CMake module not found: ${MODULE_PATH}") + endif() endforeach() # ----------------------------------------------------------------------------- # Options # ----------------------------------------------------------------------------- option(USE_VCPKG "Use vcpkg package manager" OFF) -set(USE_VCPKG OFF CACHE BOOL "Force disable vcpkg" FORCE) +set(USE_VCPKG + OFF + CACHE BOOL "Force disable vcpkg" FORCE) if(WIN32 AND CMAKE_CXX_COMPILER_ID MATCHES "MSVC") - # Temporarily disable vcpkg due to network issues - # set(USE_VCPKG ON CACHE BOOL "Enable vcpkg for MSVC" FORCE) - # message(STATUS "Enabling vcpkg for MSVC builds") + # Temporarily disable vcpkg due to network issues set(USE_VCPKG ON CACHE BOOL + # "Enable vcpkg for MSVC" FORCE) message(STATUS "Enabling vcpkg for MSVC + # builds") else() - # Keep user's preference for other platforms + # Keep user's preference for other platforms endif() option(UPDATE_VCPKG_BASELINE "Update vcpkg baseline to latest" OFF) option(ATOM_BUILD_EXAMPLES "Build examples" ON) -option(ATOM_BUILD_EXAMPLES_SELECTIVE "Enable selective building of example modules" OFF) +option(ATOM_BUILD_EXAMPLES_SELECTIVE + "Enable selective building of example modules" OFF) option(ATOM_BUILD_TESTS "Build tests" OFF) -option(ATOM_BUILD_TESTS_SELECTIVE "Enable selective building of test modules" OFF) +option(ATOM_BUILD_TESTS_SELECTIVE "Enable selective building of test modules" + OFF) option(ATOM_BUILD_PYTHON_BINDINGS "Build Python bindings" OFF) option(ATOM_BUILD_DOCS "Build documentation" OFF) option(ATOM_USE_BOOST "Enable Boost high-performance data structures" OFF) @@ -73,14 +73,33 @@ option(ATOM_USE_BOOST_LOCKFREE "Enable Boost lock-free data structures" OFF) option(ATOM_USE_BOOST_CONTAINER "Enable Boost container library" OFF) option(ATOM_USE_BOOST_GRAPH "Enable Boost graph library" OFF) option(ATOM_USE_BOOST_INTRUSIVE "Enable Boost intrusive containers" OFF) -option(ATOM_USE_PYBIND11 "Enable pybind11 support" ${ATOM_BUILD_PYTHON_BINDINGS}) +option(ATOM_USE_PYBIND11 "Enable pybind11 support" + ${ATOM_BUILD_PYTHON_BINDINGS}) option(ATOM_USE_SSH "Enable SSH support" OFF) option(ATOM_BUILD_ALL "Build all Atom modules" ON) # Module build options -foreach(MODULE - ALGORITHM ASYNC COMPONENTS CONNECTION CONTAINERS ERROR IMAGE IO LOG MEMORY - META SEARCH SECRET SERIAL SYSINFO SYSTEM TYPE UTILS WEB) +foreach( + MODULE + ALGORITHM + ASYNC + COMPONENTS + CONNECTION + CONTAINERS + ERROR + IMAGE + IO + LOG + MEMORY + META + SEARCH + SECRET + SERIAL + SYSINFO + SYSTEM + TYPE + UTILS + WEB) option(ATOM_BUILD_${MODULE} "Build ${MODULE} module" ${ATOM_BUILD_ALL}) endforeach() @@ -102,10 +121,10 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "19.29") set(CMAKE_CXX_STANDARD 20) else() - set(CMAKE_CXX_STANDARD 20) # Use C++20 for now until C++23 is stable + set(CMAKE_CXX_STANDARD 20) # Use C++20 for now until C++23 is stable endif() else() - set(CMAKE_CXX_STANDARD 20) # Safe fallback + set(CMAKE_CXX_STANDARD 20) # Safe fallback endif() set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) @@ -115,60 +134,51 @@ set(CMAKE_CXX_EXTENSIONS OFF) # ----------------------------------------------------------------------------- # Setup compiler-specific options based on build type and compiler if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") - message(STATUS "Configuring for MSVC compiler") - # Call compiler configuration from compiler_options.cmake - setup_project_defaults( - CXX_STANDARD ${CMAKE_CXX_STANDARD} - MIN_MSVC_VERSION 19.28 - ENABLE_PCH - PCH_HEADERS - ) + message(STATUS "Configuring for MSVC compiler") + # Call compiler configuration from compiler_options.cmake + setup_project_defaults( + CXX_STANDARD + ${CMAKE_CXX_STANDARD} + MIN_MSVC_VERSION + 19.28 + ENABLE_PCH + PCH_HEADERS + + + + ) else() - message(STATUS "Configuring for non-MSVC compiler: ${CMAKE_CXX_COMPILER_ID}") - # Call compiler configuration for other compilers - setup_project_defaults( - CXX_STANDARD ${CMAKE_CXX_STANDARD} - MIN_GCC_VERSION 10.0 - MIN_CLANG_VERSION 10.0 - ) + message(STATUS "Configuring for non-MSVC compiler: ${CMAKE_CXX_COMPILER_ID}") + # Call compiler configuration for other compilers + setup_project_defaults(CXX_STANDARD ${CMAKE_CXX_STANDARD} MIN_GCC_VERSION + 10.0 MIN_CLANG_VERSION 10.0) endif() # ----------------------------------------------------------------------------- # Version Definitions # ----------------------------------------------------------------------------- -add_compile_definitions( - ATOM_VERSION="${PROJECT_VERSION}" - ATOM_VERSION_STRING="${PROJECT_VERSION}" -) +add_compile_definitions(ATOM_VERSION="${PROJECT_VERSION}" + ATOM_VERSION_STRING="${PROJECT_VERSION}") # Windows API version definitions if(WIN32) - add_compile_definitions( - _WIN32_WINNT=0x0A00 # Windows 10 - WINVER=0x0A00 - _WIN32_WINDOWS=0x0A00 - ) + add_compile_definitions(_WIN32_WINNT=0x0A00 # Windows 10 + WINVER=0x0A00 _WIN32_WINDOWS=0x0A00) endif() # ----------------------------------------------------------------------------- # Include Directories # ----------------------------------------------------------------------------- -include_directories( - ${CMAKE_CURRENT_SOURCE_DIR}/extra - ${CMAKE_CURRENT_BINARY_DIR} - ${CMAKE_CURRENT_SOURCE_DIR} - . -) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/extra + ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} .) # ----------------------------------------------------------------------------- # Custom Targets # ----------------------------------------------------------------------------- add_custom_target( AtomCmakeAdditionalFiles - SOURCES - ${CMAKE_CURRENT_SOURCE_DIR}/cmake/CompilerOptions.cmake - ${CMAKE_CURRENT_SOURCE_DIR}/cmake/GitVersion.cmake -) + SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/cmake/CompilerOptions.cmake + ${CMAKE_CURRENT_SOURCE_DIR}/cmake/GitVersion.cmake) # ----------------------------------------------------------------------------- # Package Management @@ -192,10 +202,10 @@ message(STATUS "Finding dependency packages...") # Use standardized dependency finding if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") - message(STATUS "Using MSVC-specific dependency finding") - include(cmake/FindDependenciesMSVC.cmake) + message(STATUS "Using MSVC-specific dependency finding") + include(cmake/FindDependenciesMSVC.cmake) else() - include(cmake/FindDependencies.cmake) + include(cmake/FindDependencies.cmake) endif() # SSH support and Python bindings are now handled by FindDependencies.cmake @@ -204,51 +214,31 @@ endif() # Automatic Dependency Resolution # ----------------------------------------------------------------------------- if(ATOM_AUTO_RESOLVE_DEPS) - message(STATUS "Automatic dependency resolution enabled") - include(cmake/ScanModule.cmake) - atom_resolve_all_dependencies() + message(STATUS "Automatic dependency resolution enabled") + include(cmake/ScanModule.cmake) + atom_resolve_all_dependencies() endif() -# Skip platform-specific dependencies for now -# Linux/WSL/Windows platform-specific dependencies -# if(LINUX) -# find_package(X11 REQUIRED) -# if(X11_FOUND) -# include_directories(${X11_INCLUDE_DIR}) -# else() -# message(FATAL_ERROR "X11 development files not found. Please install libx11-dev or equivalent.") -# endif() -# find_package(PkgConfig REQUIRED) -# pkg_check_modules(UDEV REQUIRED libudev) -# if(UDEV_FOUND) -# include_directories(${UDEV_INCLUDE_DIRS}) -# link_directories(${UDEV_LIBRARY_DIRS}) -# else() -# message(FATAL_ERROR "libudev development files not found. Please install libudev-dev or equivalent.") -# endif() -# endif() - -# Skip Boost for now -# Boost -# if(ATOM_USE_BOOST) -# set(Boost_USE_STATIC_LIBS ON) -# set(Boost_USE_MULTITHREADED ON) -# set(Boost_USE_STATIC_RUNTIME OFF) -# set(BOOST_COMPONENTS) -# if(ATOM_USE_BOOST_CONTAINER) -# list(APPEND BOOST_COMPONENTS container) -# endif() -# if(ATOM_USE_BOOST_LOCKFREE) -# list(APPEND BOOST_COMPONENTS atomic thread) -# endif() -# if(ATOM_USE_BOOST_GRAPH) -# list(APPEND BOOST_COMPONENTS graph) -# endif() -# # intrusive is header-only -# find_package(Boost 1.74 REQUIRED COMPONENTS ${BOOST_COMPONENTS}) -# include_directories(${Boost_INCLUDE_DIRS}) -# message(STATUS "Found Boost: ${Boost_VERSION} (${Boost_INCLUDE_DIRS})") -# endif() +# Skip platform-specific dependencies for now Linux/WSL/Windows +# platform-specific dependencies if(LINUX) find_package(X11 REQUIRED) +# if(X11_FOUND) include_directories(${X11_INCLUDE_DIR}) else() +# message(FATAL_ERROR "X11 development files not found. Please install +# libx11-dev or equivalent.") endif() find_package(PkgConfig REQUIRED) +# pkg_check_modules(UDEV REQUIRED libudev) if(UDEV_FOUND) +# include_directories(${UDEV_INCLUDE_DIRS}) +# link_directories(${UDEV_LIBRARY_DIRS}) else() message(FATAL_ERROR "libudev +# development files not found. Please install libudev-dev or equivalent.") +# endif() endif() + +# Skip Boost for now Boost if(ATOM_USE_BOOST) set(Boost_USE_STATIC_LIBS ON) +# set(Boost_USE_MULTITHREADED ON) set(Boost_USE_STATIC_RUNTIME OFF) +# set(BOOST_COMPONENTS) if(ATOM_USE_BOOST_CONTAINER) list(APPEND +# BOOST_COMPONENTS container) endif() if(ATOM_USE_BOOST_LOCKFREE) list(APPEND +# BOOST_COMPONENTS atomic thread) endif() if(ATOM_USE_BOOST_GRAPH) list(APPEND +# BOOST_COMPONENTS graph) endif() # intrusive is header-only find_package(Boost +# 1.74 REQUIRED COMPONENTS ${BOOST_COMPONENTS}) +# include_directories(${Boost_INCLUDE_DIRS}) message(STATUS "Found Boost: +# ${Boost_VERSION} (${Boost_INCLUDE_DIRS})") endif() # ----------------------------------------------------------------------------- # Version Info Header @@ -257,18 +247,18 @@ endif() configure_atom_version(VERSION_VARIABLE PROJECT_VERSION) # Also configure the version info header -configure_file( - ${CMAKE_CURRENT_SOURCE_DIR}/cmake/version_info.h.in - ${CMAKE_CURRENT_BINARY_DIR}/atom_version_info.h - @ONLY -) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/version_info.h.in + ${CMAKE_CURRENT_BINARY_DIR}/atom_version_info.h @ONLY) # ----------------------------------------------------------------------------- # Ninja Generator Support # ----------------------------------------------------------------------------- if(CMAKE_GENERATOR STREQUAL "Ninja" OR CMAKE_GENERATOR MATCHES "Ninja") - message(STATUS "Ninja generator detected. Enabling Ninja-specific optimizations.") - set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE BOOL "Enable compile_commands.json for Ninja" FORCE) + message( + STATUS "Ninja generator detected. Enabling Ninja-specific optimizations.") + set(CMAKE_EXPORT_COMPILE_COMMANDS + ON + CACHE BOOL "Enable compile_commands.json for Ninja" FORCE) endif() # ----------------------------------------------------------------------------- @@ -287,47 +277,35 @@ if(ATOM_BUILD_TESTS) add_subdirectory(tests) endif() -# Add minimal timer test for debugging -# Temporarily commented out due to missing source files -# add_executable(test_timer_minimal_isolated test_timer_minimal_isolated.cpp) -# target_link_libraries(test_timer_minimal_isolated -# PRIVATE -# atom-async -# atom-utils -# atom-error -# ) -# target_compile_features(test_timer_minimal_isolated PRIVATE cxx_std_20) -# target_include_directories(test_timer_minimal_isolated PRIVATE ${CMAKE_SOURCE_DIR}) - -# Add header-only test -# Temporarily commented out due to missing source files +# Add minimal timer test for debugging Temporarily commented out due to missing +# source files add_executable(test_timer_minimal_isolated +# test_timer_minimal_isolated.cpp) +# target_link_libraries(test_timer_minimal_isolated PRIVATE atom-async +# atom-utils atom-error ) target_compile_features(test_timer_minimal_isolated +# PRIVATE cxx_std_20) target_include_directories(test_timer_minimal_isolated +# PRIVATE ${CMAKE_SOURCE_DIR}) + +# Add header-only test Temporarily commented out due to missing source files # add_executable(test_timer_header_only test_timer_header_only.cpp) -# target_link_libraries(test_timer_header_only -# PRIVATE -# atom-async -# atom-utils -# atom-error -# ) -# target_compile_features(test_timer_header_only PRIVATE cxx_std_20) -# target_include_directories(test_timer_header_only PRIVATE ${CMAKE_SOURCE_DIR}) - -# Add constructor-only test -# Temporarily commented out due to missing source files -# add_executable(test_timer_constructor_only test_timer_constructor_only.cpp) -# target_link_libraries(test_timer_constructor_only -# PRIVATE -# atom-async -# atom-utils -# atom-error -# ) -# target_compile_features(test_timer_constructor_only PRIVATE cxx_std_20) -# target_include_directories(test_timer_constructor_only PRIVATE ${CMAKE_SOURCE_DIR}) - -# Basic C++ test removed during cleanup - test_basic_cpp.cpp was a temporary debugging file - -# Add minimal timer test (no Atom dependencies) -# Temporarily commented out due to missing source files -# add_executable(test_minimal_timer_isolated test_minimal_timer_isolated.cpp) +# target_link_libraries(test_timer_header_only PRIVATE atom-async atom-utils +# atom-error ) target_compile_features(test_timer_header_only PRIVATE +# cxx_std_20) target_include_directories(test_timer_header_only PRIVATE +# ${CMAKE_SOURCE_DIR}) + +# Add constructor-only test Temporarily commented out due to missing source +# files add_executable(test_timer_constructor_only +# test_timer_constructor_only.cpp) +# target_link_libraries(test_timer_constructor_only PRIVATE atom-async +# atom-utils atom-error ) target_compile_features(test_timer_constructor_only +# PRIVATE cxx_std_20) target_include_directories(test_timer_constructor_only +# PRIVATE ${CMAKE_SOURCE_DIR}) + +# Basic C++ test removed during cleanup - test_basic_cpp.cpp was a temporary +# debugging file + +# Add minimal timer test (no Atom dependencies) Temporarily commented out due to +# missing source files add_executable(test_minimal_timer_isolated +# test_minimal_timer_isolated.cpp) # target_compile_features(test_minimal_timer_isolated PRIVATE cxx_std_20) # Add isolated timer test (no Atom dependencies, more complete implementation) @@ -355,19 +333,22 @@ endif() # Register all Atom components for modular installation (if function exists) if(COMMAND atom_register_component) - foreach(MODULE ${ATOM_MODULES}) - string(TOLOWER ${MODULE} MODULE_LOWER) - atom_register_component(${MODULE_LOWER} - DESCRIPTION "Atom ${MODULE} module" - VERSION ${PROJECT_VERSION} - DEPENDS ${ATOM_MODULE_DEPS_${MODULE}} - ) - endforeach() - - # Setup modular installation system - if(COMMAND atom_setup_modular_installation) - atom_setup_modular_installation() - endif() + foreach(MODULE ${ATOM_MODULES}) + string(TOLOWER ${MODULE} MODULE_LOWER) + atom_register_component( + ${MODULE_LOWER} + DESCRIPTION + "Atom ${MODULE} module" + VERSION + ${PROJECT_VERSION} + DEPENDS + ${ATOM_MODULE_DEPS_${MODULE}}) + endforeach() + + # Setup modular installation system + if(COMMAND atom_setup_modular_installation) + atom_setup_modular_installation() + endif() endif() # ----------------------------------------------------------------------------- @@ -376,12 +357,12 @@ endif() # Setup CPack for package generation (if function exists) if(COMMAND atom_setup_cpack) - atom_setup_cpack() + atom_setup_cpack() endif() # Create modular packages if(ATOM_INSTALL_COMPONENT_PACKAGES AND COMMAND atom_create_modular_packages) - atom_create_modular_packages() + atom_create_modular_packages() endif() # ----------------------------------------------------------------------------- @@ -391,15 +372,14 @@ include(GNUInstallDirs) install( DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/atom/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/atom - FILES_MATCHING PATTERN "*.h" PATTERN "*.hpp" + FILES_MATCHING + PATTERN "*.h" + PATTERN "*.hpp" PATTERN "**/internal" EXCLUDE PATTERN "**/tests" EXCLUDE - PATTERN "**/example" EXCLUDE -) -install( - FILES ${CMAKE_CURRENT_BINARY_DIR}/atom_version_info.h - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/atom -) + PATTERN "**/example" EXCLUDE) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/atom_version_info.h + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/atom) # ----------------------------------------------------------------------------- # IDE Folders & Final Message diff --git a/README.md b/README.md index d7632569..0dad6a85 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,3 @@ # Atom + The foundational library for all elemental astro projects diff --git a/WARP.md b/WARP.md index 344c134a..df904f6a 100644 --- a/WARP.md +++ b/WARP.md @@ -9,6 +9,7 @@ The **Atom** library is a modular C++20/C++23 foundational library for astronomi ### Module Structure & Dependencies Each module follows this standardized pattern: + ``` atom// ├── CMakeLists.txt # Module build config with dependency checks @@ -32,6 +33,7 @@ Build order: `atom-error` → `atom-log` → `atom-meta`/`atom-utils` → specia ### Component Architecture Pattern The library uses a sophisticated component registry system for dependency injection and lifecycle management: + - **Registry Pattern**: Central `Registry` class manages all components with thread-safe operations - **Lifecycle Management**: `LifecycleManager` handles component initialization order and dependency resolution - **Dependency Injection**: Components can declare required/optional dependencies that are auto-resolved @@ -40,6 +42,7 @@ The library uses a sophisticated component registry system for dependency inject ## Build Commands ### CMake (Primary) + ```bash # Configure with preset (recommended) cmake --preset release @@ -57,6 +60,7 @@ cmake --build build --parallel 8 # Parallel build ``` ### Cross-Platform Scripts (Recommended) + ```bash # Unix/Linux/macOS - Enhanced build script ./build.sh --release --tests --examples --jobs 8 @@ -69,6 +73,7 @@ build.bat --debug --run-tests --docs ``` ### XMake (Alternative) + ```bash xmake f --build_examples=y --build_tests=y --python=y xmake build @@ -77,6 +82,7 @@ xmake install # Install built libraries ``` ### Python Development + ```bash pip install -e .[dev] pytest -q # Run Python tests @@ -85,6 +91,7 @@ pytest -q # Run Python tests ## Module-Specific Development ### Adding New Modules + 1. Create module directory under `atom/` 2. Add dependency entry in `cmake/module_dependencies.cmake` 3. Update `ATOM_MODULE_BUILD_ORDER` @@ -92,7 +99,9 @@ pytest -q # Run Python tests 5. Add example in `example/` if public-facing ### Dependency Management + Dependencies are auto-resolved via CMake. Each module's `CMakeLists.txt` includes: + ```cmake foreach(dep ${ATOM__DEPENDS}) string(REPLACE "atom-" "ATOM_BUILD_" dep_var_name ${dep}) @@ -103,6 +112,7 @@ endforeach() ## Testing ### C++ Tests + ```bash # Debug build with tests cmake --preset debug && cmake --build --preset debug -j @@ -119,11 +129,13 @@ xmake test ``` ### Test Organization + - **Unit Tests**: `tests//test_*.hpp` with GoogleTest framework - **Integration Tests**: Uses `atom/tests/test.hpp` custom registration system - **Examples**: `example//*.cpp` - one executable per file ### Test Registration Pattern + ```cpp // Custom test registration in atom/tests/test.hpp ATOM_INLINE void registerTest(std::string name, std::function func, @@ -136,23 +148,30 @@ ATOM_INLINE void registerTest(std::string name, std::function func, ## Key Development Patterns ### Platform Detection + Use macros from `atom/macro.hpp`: + - `ATOM_PLATFORM_WINDOWS/LINUX/APPLE` for platform detection - `ATOM_USE_BOOST*` flags for Boost integration - Prefer existing macros over raw `#ifdef` ### Error Handling + All modules depend on `atom-error`: + - Use `Result` types from `atom-error`, not raw exceptions - Follow RAII principles with smart pointers ### Logging + Use `atom-log` structured logging instead of `std::cout` ### Async Operations + `atom-async` provides async primitives - don't reinvent async functionality ### Module Integration Points + - **Error Handling**: `atom-error` - use result types - **Logging**: `atom-log` - structured logging - **Async Operations**: `atom-async` - async primitives @@ -161,6 +180,7 @@ Use `atom-log` structured logging instead of `std::cout` ## Build Configuration ### Key Build Options + - `ATOM_BUILD_EXAMPLES=ON` - Build example applications - `ATOM_BUILD_TESTS=ON` - Build test suite - `ATOM_BUILD_PYTHON_BINDINGS=ON` - Enable Python bindings @@ -168,6 +188,7 @@ Use `atom-log` structured logging instead of `std::cout` - Individual module flags: `ATOM_BUILD_=ON` ### Build System Features + - **Ninja Generator**: Automatically used if available for faster builds - **Parallel Builds**: Scripts auto-detect CPU cores - **Cross-Platform**: Windows (MSVC), Linux (GCC), macOS (Clang) @@ -176,11 +197,13 @@ Use `atom-log` structured logging instead of `std::cout` ## Code Standards ### Language Requirements + - **C++20 minimum**, C++23 preferred (auto-detected based on compiler) - Extensive use of concepts, ranges, source_location - Template-heavy design with meta-programming in `atom/meta/` ### Naming Conventions (per STYLE_OF_CODE.md) + - **Variables/Functions**: camelCase - **Classes/Namespaces**: PascalCase - **Constants**: UPPER_SNAKE_CASE @@ -188,18 +211,21 @@ Use `atom-log` structured logging instead of `std::cout` - **Class members**: m_prefix for private variables ### Documentation + - Prefer Doxygen format: `@brief`, `@param`, `@return` - Comments should explain purpose and context ## File Structure Patterns ### Important Files + - **Version Info**: `cmake/version_info.h.in` → `build/atom_version_info.h` - **Platform Config**: `cmake/PlatformSpecifics.cmake` - **Compiler Options**: `cmake/compiler_options.cmake` - **External Deps**: `vcpkg.json` and XMake `add_requires()` ### Python Bindings + - Located in `python/` with pybind11 - Auto-detects module types from directory structure - Each module gets its own Python binding file @@ -207,24 +233,28 @@ Use `atom-log` structured logging instead of `std::cout` ## Common Development Tasks ### Documentation Generation + ```bash doxygen Doxyfile # C++ docs sphinx-build -b html docs docs/_build # Python docs ``` ### Code Formatting + ```bash clang-format -i **/*.cpp **/*.hpp # Use .clang-format config pre-commit run -a # Python formatting (Black, isort, Ruff, MyPy) ``` ### Package Management & Installation + - **C++ Dependencies**: Via vcpkg/Conan (currently disabled by default) - **Python Dependencies**: Via pip/conda - **Modular Installation**: `scripts/modular-installer.py` for component-wise installation - **System Dependencies**: `./build.sh --install-deps` auto-installs required packages ### Modular Installation System + ```bash # Install specific components with dependency resolution python scripts/modular-installer.py install core networking diff --git a/atom/CMakeLists.txt b/atom/CMakeLists.txt index 4e87ae77..693b269e 100644 --- a/atom/CMakeLists.txt +++ b/atom/CMakeLists.txt @@ -1,13 +1,14 @@ -# CMakeLists.txt for Atom -# This project is licensed under the terms of the GPL3 license. +# CMakeLists.txt for Atom This project is licensed under the terms of the GPL3 +# license. # -# Project Name: Atom -# Description: Atom Library for all of the Element Astro Project -# Author: Max Qian -# License: GPL3 +# Project Name: Atom Description: Atom Library for all of the Element Astro +# Project Author: Max Qian License: GPL3 cmake_minimum_required(VERSION 3.20) -project(atom VERSION 1.0.0 LANGUAGES C CXX) +project( + atom + VERSION 1.0.0 + LANGUAGES C CXX) # ============================================================================= # Python Support Configuration @@ -15,18 +16,22 @@ project(atom VERSION 1.0.0 LANGUAGES C CXX) option(ATOM_BUILD_PYTHON "Build Atom with Python support" OFF) if(ATOM_BUILD_PYTHON) - find_package(Python COMPONENTS Interpreter Development REQUIRED) - if(PYTHON_FOUND) - message(STATUS "Found Python ${PYTHON_VERSION_STRING}: ${PYTHON_EXECUTABLE}") - find_package(pybind11 QUIET) - if(pybind11_FOUND) - message(STATUS "Found pybind11: ${pybind11_INCLUDE_DIRS}") - else() - message(FATAL_ERROR "pybind11 not found") - endif() + find_package( + Python + COMPONENTS Interpreter Development + REQUIRED) + if(PYTHON_FOUND) + message( + STATUS "Found Python ${PYTHON_VERSION_STRING}: ${PYTHON_EXECUTABLE}") + find_package(pybind11 QUIET) + if(pybind11_FOUND) + message(STATUS "Found pybind11: ${pybind11_INCLUDE_DIRS}") else() - message(FATAL_ERROR "Python not found") + message(FATAL_ERROR "pybind11 not found") endif() + else() + message(FATAL_ERROR "Python not found") + endif() endif() # ============================================================================= @@ -34,11 +39,11 @@ endif() # ============================================================================= if(UNIX AND NOT APPLE) - # Linux-specific dependencies - pkg_check_modules(SYSTEMD REQUIRED libsystemd) - if(SYSTEMD_FOUND) - message(STATUS "Found libsystemd: ${SYSTEMD_VERSION}") - endif() + # Linux-specific dependencies + pkg_check_modules(SYSTEMD REQUIRED libsystemd) + if(SYSTEMD_FOUND) + message(STATUS "Found libsystemd: ${SYSTEMD_VERSION}") + endif() endif() # ============================================================================= @@ -47,17 +52,26 @@ endif() # Function to check if a module directory is valid function(check_module_directory module_name dir_name result_var) - set(module_path "${CMAKE_CURRENT_SOURCE_DIR}/${dir_name}") - if(EXISTS "${module_path}" AND EXISTS "${module_path}/CMakeLists.txt") - set(${result_var} TRUE PARENT_SCOPE) - else() - set(${result_var} FALSE PARENT_SCOPE) - if(NOT EXISTS "${module_path}") - message(STATUS "Module directory for '${module_name}' does not exist: ${module_path}") - elseif(NOT EXISTS "${module_path}/CMakeLists.txt") - message(STATUS "Module directory '${module_path}' exists but lacks CMakeLists.txt") - endif() + set(module_path "${CMAKE_CURRENT_SOURCE_DIR}/${dir_name}") + if(EXISTS "${module_path}" AND EXISTS "${module_path}/CMakeLists.txt") + set(${result_var} + TRUE + PARENT_SCOPE) + else() + set(${result_var} + FALSE + PARENT_SCOPE) + if(NOT EXISTS "${module_path}") + message( + STATUS + "Module directory for '${module_name}' does not exist: ${module_path}" + ) + elseif(NOT EXISTS "${module_path}/CMakeLists.txt") + message( + STATUS + "Module directory '${module_path}' exists but lacks CMakeLists.txt") endif() + endif() endfunction() # List of subdirectories to build @@ -65,198 +79,203 @@ set(SUBDIRECTORIES) # Check if each module needs to be built and add to the list if(ATOM_BUILD_ALGORITHM) - check_module_directory("algorithm" "algorithm" ALGORITHM_VALID) - if(ALGORITHM_VALID) - list(APPEND SUBDIRECTORIES algorithm) - message(STATUS "Building algorithm module") - else() - message(STATUS "Skipping algorithm module due to missing or invalid directory") - endif() + check_module_directory("algorithm" "algorithm" ALGORITHM_VALID) + if(ALGORITHM_VALID) + list(APPEND SUBDIRECTORIES algorithm) + message(STATUS "Building algorithm module") + else() + message( + STATUS "Skipping algorithm module due to missing or invalid directory") + endif() endif() if(ATOM_BUILD_ASYNC) - check_module_directory("async" "async" ASYNC_VALID) - if(ASYNC_VALID) - list(APPEND SUBDIRECTORIES async) - message(STATUS "Building async module") - else() - message(STATUS "Skipping async module due to missing or invalid directory") - endif() + check_module_directory("async" "async" ASYNC_VALID) + if(ASYNC_VALID) + list(APPEND SUBDIRECTORIES async) + message(STATUS "Building async module") + else() + message(STATUS "Skipping async module due to missing or invalid directory") + endif() endif() if(ATOM_BUILD_COMPONENTS) - check_module_directory("components" "components" COMPONENTS_VALID) - if(COMPONENTS_VALID) - list(APPEND SUBDIRECTORIES components) - message(STATUS "Building components module") - else() - message(STATUS "Skipping components module due to missing or invalid directory") - endif() + check_module_directory("components" "components" COMPONENTS_VALID) + if(COMPONENTS_VALID) + list(APPEND SUBDIRECTORIES components) + message(STATUS "Building components module") + else() + message( + STATUS "Skipping components module due to missing or invalid directory") + endif() endif() if(ATOM_BUILD_CONNECTION) - check_module_directory("connection" "connection" CONNECTION_VALID) - if(CONNECTION_VALID) - list(APPEND SUBDIRECTORIES connection) - message(STATUS "Building connection module") - else() - message(STATUS "Skipping connection module due to missing or invalid directory") - endif() + check_module_directory("connection" "connection" CONNECTION_VALID) + if(CONNECTION_VALID) + list(APPEND SUBDIRECTORIES connection) + message(STATUS "Building connection module") + else() + message( + STATUS "Skipping connection module due to missing or invalid directory") + endif() endif() if(ATOM_BUILD_CONTAINERS) - check_module_directory("containers" "containers" CONTAINERS_VALID) - if(CONTAINERS_VALID) - list(APPEND SUBDIRECTORIES containers) - message(STATUS "Building containers module") - else() - message(STATUS "Skipping containers module due to missing or invalid directory") - endif() + check_module_directory("containers" "containers" CONTAINERS_VALID) + if(CONTAINERS_VALID) + list(APPEND SUBDIRECTORIES containers) + message(STATUS "Building containers module") + else() + message( + STATUS "Skipping containers module due to missing or invalid directory") + endif() endif() if(ATOM_BUILD_ERROR) - check_module_directory("error" "error" ERROR_VALID) - if(ERROR_VALID) - list(APPEND SUBDIRECTORIES error) - message(STATUS "Building error module") - else() - message(STATUS "Skipping error module due to missing or invalid directory") - endif() + check_module_directory("error" "error" ERROR_VALID) + if(ERROR_VALID) + list(APPEND SUBDIRECTORIES error) + message(STATUS "Building error module") + else() + message(STATUS "Skipping error module due to missing or invalid directory") + endif() endif() if(ATOM_BUILD_IMAGE) - check_module_directory("image" "image" IMAGE_VALID) - if(IMAGE_VALID) - list(APPEND SUBDIRECTORIES image) - message(STATUS "Building image module") - else() - message(STATUS "Skipping image module due to missing or invalid directory") - endif() + check_module_directory("image" "image" IMAGE_VALID) + if(IMAGE_VALID) + list(APPEND SUBDIRECTORIES image) + message(STATUS "Building image module") + else() + message(STATUS "Skipping image module due to missing or invalid directory") + endif() endif() if(ATOM_BUILD_IO) - check_module_directory("io" "io" IO_VALID) - if(IO_VALID) - list(APPEND SUBDIRECTORIES io) - message(STATUS "Building io module") - else() - message(STATUS "Skipping io module due to missing or invalid directory") - endif() + check_module_directory("io" "io" IO_VALID) + if(IO_VALID) + list(APPEND SUBDIRECTORIES io) + message(STATUS "Building io module") + else() + message(STATUS "Skipping io module due to missing or invalid directory") + endif() endif() if(ATOM_BUILD_LOG) - check_module_directory("log" "log" LOG_VALID) - if(LOG_VALID) - list(APPEND SUBDIRECTORIES log) - message(STATUS "Building log module") - else() - message(STATUS "Skipping log module due to missing or invalid directory") - endif() + check_module_directory("log" "log" LOG_VALID) + if(LOG_VALID) + list(APPEND SUBDIRECTORIES log) + message(STATUS "Building log module") + else() + message(STATUS "Skipping log module due to missing or invalid directory") + endif() endif() if(ATOM_BUILD_MEMORY) - check_module_directory("memory" "memory" MEMORY_VALID) - if(MEMORY_VALID) - list(APPEND SUBDIRECTORIES memory) - message(STATUS "Building memory module") - else() - message(STATUS "Skipping memory module due to missing or invalid directory") - endif() + check_module_directory("memory" "memory" MEMORY_VALID) + if(MEMORY_VALID) + list(APPEND SUBDIRECTORIES memory) + message(STATUS "Building memory module") + else() + message(STATUS "Skipping memory module due to missing or invalid directory") + endif() endif() if(ATOM_BUILD_META) - check_module_directory("meta" "meta" META_VALID) - if(META_VALID) - list(APPEND SUBDIRECTORIES meta) - message(STATUS "Building meta module") - else() - message(STATUS "Skipping meta module due to missing or invalid directory") - endif() + check_module_directory("meta" "meta" META_VALID) + if(META_VALID) + list(APPEND SUBDIRECTORIES meta) + message(STATUS "Building meta module") + else() + message(STATUS "Skipping meta module due to missing or invalid directory") + endif() endif() if(ATOM_BUILD_SEARCH) - check_module_directory("search" "search" SEARCH_VALID) - if(SEARCH_VALID) - list(APPEND SUBDIRECTORIES search) - message(STATUS "Building search module") - else() - message(STATUS "Skipping search module due to missing or invalid directory") - endif() + check_module_directory("search" "search" SEARCH_VALID) + if(SEARCH_VALID) + list(APPEND SUBDIRECTORIES search) + message(STATUS "Building search module") + else() + message(STATUS "Skipping search module due to missing or invalid directory") + endif() endif() if(ATOM_BUILD_SECRET) - check_module_directory("secret" "secret" SECRET_VALID) - if(SECRET_VALID) - list(APPEND SUBDIRECTORIES secret) - message(STATUS "Building secret module") - else() - message(STATUS "Skipping secret module due to missing or invalid directory") - endif() + check_module_directory("secret" "secret" SECRET_VALID) + if(SECRET_VALID) + list(APPEND SUBDIRECTORIES secret) + message(STATUS "Building secret module") + else() + message(STATUS "Skipping secret module due to missing or invalid directory") + endif() endif() if(ATOM_BUILD_SERIAL) - check_module_directory("serial" "serial" SERIAL_VALID) - if(SERIAL_VALID) - list(APPEND SUBDIRECTORIES serial) - message(STATUS "Building serial module") - else() - message(STATUS "Skipping serial module due to missing or invalid directory") - endif() + check_module_directory("serial" "serial" SERIAL_VALID) + if(SERIAL_VALID) + list(APPEND SUBDIRECTORIES serial) + message(STATUS "Building serial module") + else() + message(STATUS "Skipping serial module due to missing or invalid directory") + endif() endif() if(ATOM_BUILD_SYSINFO) - check_module_directory("sysinfo" "sysinfo" SYSINFO_VALID) - if(SYSINFO_VALID) - list(APPEND SUBDIRECTORIES sysinfo) - message(STATUS "Building sysinfo module") - else() - message(STATUS "Skipping sysinfo module due to missing or invalid directory") - endif() + check_module_directory("sysinfo" "sysinfo" SYSINFO_VALID) + if(SYSINFO_VALID) + list(APPEND SUBDIRECTORIES sysinfo) + message(STATUS "Building sysinfo module") + else() + message( + STATUS "Skipping sysinfo module due to missing or invalid directory") + endif() endif() if(ATOM_BUILD_SYSTEM) - check_module_directory("system" "system" SYSTEM_VALID) - if(SYSTEM_VALID) - list(APPEND SUBDIRECTORIES system) - message(STATUS "Building system module") - else() - message(STATUS "Skipping system module due to missing or invalid directory") - endif() + check_module_directory("system" "system" SYSTEM_VALID) + if(SYSTEM_VALID) + list(APPEND SUBDIRECTORIES system) + message(STATUS "Building system module") + else() + message(STATUS "Skipping system module due to missing or invalid directory") + endif() endif() if(ATOM_BUILD_TYPE) - check_module_directory("type" "type" TYPE_VALID) - if(TYPE_VALID) - list(APPEND SUBDIRECTORIES type) - message(STATUS "Building type module") - else() - message(STATUS "Skipping type module due to missing or invalid directory") - endif() + check_module_directory("type" "type" TYPE_VALID) + if(TYPE_VALID) + list(APPEND SUBDIRECTORIES type) + message(STATUS "Building type module") + else() + message(STATUS "Skipping type module due to missing or invalid directory") + endif() endif() if(ATOM_BUILD_UTILS) - check_module_directory("utils" "utils" UTILS_VALID) - if(UTILS_VALID) - list(APPEND SUBDIRECTORIES utils) - message(STATUS "Building utils module") - else() - message(STATUS "Skipping utils module due to missing or invalid directory") - endif() + check_module_directory("utils" "utils" UTILS_VALID) + if(UTILS_VALID) + list(APPEND SUBDIRECTORIES utils) + message(STATUS "Building utils module") + else() + message(STATUS "Skipping utils module due to missing or invalid directory") + endif() endif() if(ATOM_BUILD_WEB) - check_module_directory("web" "web" WEB_VALID) - if(WEB_VALID) - list(APPEND SUBDIRECTORIES web) - message(STATUS "Building web module") - else() - message(STATUS "Skipping web module due to missing or invalid directory") - endif() + check_module_directory("web" "web" WEB_VALID) + if(WEB_VALID) + list(APPEND SUBDIRECTORIES web) + message(STATUS "Building web module") + else() + message(STATUS "Skipping web module due to missing or invalid directory") + endif() endif() if(ATOM_BUILD_TESTS) - list(APPEND SUBDIRECTORIES tests) - message(STATUS "Building tests") + list(APPEND SUBDIRECTORIES tests) + message(STATUS "Building tests") endif() # ============================================================================= @@ -265,10 +284,10 @@ endif() # Process module dependencies (if functions are available) if(COMMAND scan_module_dependencies) - scan_module_dependencies() + scan_module_dependencies() endif() if(COMMAND process_module_dependencies) - process_module_dependencies() + process_module_dependencies() endif() # ============================================================================= @@ -277,12 +296,15 @@ endif() # Add all modules to build foreach(dir ${SUBDIRECTORIES}) - set(subdir_path "${CMAKE_CURRENT_SOURCE_DIR}/${dir}") - if(EXISTS "${subdir_path}" AND EXISTS "${subdir_path}/CMakeLists.txt") - add_subdirectory(${dir}) - else() - message(STATUS "Skipping directory '${dir}' as it does not exist or does not contain CMakeLists.txt") - endif() + set(subdir_path "${CMAKE_CURRENT_SOURCE_DIR}/${dir}") + if(EXISTS "${subdir_path}" AND EXISTS "${subdir_path}/CMakeLists.txt") + add_subdirectory(${dir}) + else() + message( + STATUS + "Skipping directory '${dir}' as it does not exist or does not contain CMakeLists.txt" + ) + endif() endforeach() # ============================================================================= @@ -290,11 +312,15 @@ endforeach() # ============================================================================= # Add extra components directory if it exists -if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/extra" AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/extra/CMakeLists.txt") - message(STATUS "Adding extra components directory") - add_subdirectory(extra) +if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/extra" + AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/extra/CMakeLists.txt") + message(STATUS "Adding extra components directory") + add_subdirectory(extra) else() - message(STATUS "Skipping extra components directory as it does not exist or does not contain CMakeLists.txt") + message( + STATUS + "Skipping extra components directory as it does not exist or does not contain CMakeLists.txt" + ) endif() # ============================================================================= @@ -302,35 +328,40 @@ endif() # ============================================================================= # Option to create a unified Atom library -option(ATOM_BUILD_UNIFIED_LIBRARY "Build a unified Atom library containing all modules" ON) +option(ATOM_BUILD_UNIFIED_LIBRARY + "Build a unified Atom library containing all modules" ON) if(ATOM_BUILD_UNIFIED_LIBRARY) - # Get all targets that are atom modules - get_property(ATOM_MODULE_TARGETS GLOBAL PROPERTY ATOM_MODULE_TARGETS) - - if(ATOM_MODULE_TARGETS) - message(STATUS "Creating unified Atom library with modules: ${ATOM_MODULE_TARGETS}") - - # Create unified target - add_library(atom-unified INTERFACE) - - # Link all module targets - target_link_libraries(atom-unified INTERFACE ${ATOM_MODULE_TARGETS}) - - # Create an alias 'atom' that points to 'atom-unified' - # This allows examples and other components to link against 'atom' - add_library(atom ALIAS atom-unified) - - # Install unified target - install(TARGETS atom-unified - EXPORT atom-unified-targets - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) - else() - message(STATUS "No module targets found for unified library") - endif() + # Get all targets that are atom modules + get_property(ATOM_MODULE_TARGETS GLOBAL PROPERTY ATOM_MODULE_TARGETS) + + if(ATOM_MODULE_TARGETS) + message( + STATUS + "Creating unified Atom library with modules: ${ATOM_MODULE_TARGETS}") + + # Create unified target + add_library(atom-unified INTERFACE) + + # Link all module targets + target_link_libraries(atom-unified INTERFACE ${ATOM_MODULE_TARGETS}) + + # Create an alias 'atom' that points to 'atom-unified' This allows examples + # and other components to link against 'atom' + add_library(atom ALIAS atom-unified) + + # Install unified target + install( + TARGETS atom-unified + EXPORT atom-unified-targets + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + INCLUDES + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + else() + message(STATUS "No module targets found for unified library") + endif() endif() message(STATUS "Atom modules configuration completed successfully") diff --git a/atom/algorithm/CMakeLists.txt b/atom/algorithm/CMakeLists.txt index cd60b8f3..c964b06d 100644 --- a/atom/algorithm/CMakeLists.txt +++ b/atom/algorithm/CMakeLists.txt @@ -1,5 +1,8 @@ cmake_minimum_required(VERSION 3.21) -project(atom-algorithm VERSION 1.0.0 LANGUAGES C CXX) +project( + atom-algorithm + VERSION 1.0.0 + LANGUAGES C CXX) # Include standardized module configuration include(${CMAKE_SOURCE_DIR}/cmake/ModuleDependencies.cmake) @@ -15,41 +18,31 @@ set(SOURCES # Core files core/algorithm.cpp core/opencl_utils.cpp - # Crypto files crypto/md5.cpp crypto/sha1.cpp crypto/blowfish.cpp crypto/tea.cpp - # Hash files hash/mhash.cpp - # Math files math/math.cpp math/fraction.cpp math/bignumber.cpp math/gpu_math.cpp - # Compression files compression/huffman.cpp compression/matrix_compress.cpp - # Signal processing files signal/convolve.cpp - # Optimization files optimization/pathfinding.cpp - # Encoding files encoding/base.cpp - # Graphics files graphics/flood.cpp - # Utils files - utils/fnmatch.cpp -) + utils/fnmatch.cpp) set(HEADERS # Backwards compatibility headers (in root) @@ -77,21 +70,17 @@ set(HEADERS snowflake.hpp tea.hpp weight.hpp - # Actual implementation headers (in subdirectories) core/algorithm.hpp core/rust_numeric.hpp core/simd_utils.hpp core/opencl_utils.hpp - crypto/md5.hpp crypto/sha1.hpp crypto/blowfish.hpp crypto/tea.hpp - hash/hash.hpp hash/mhash.hpp - math/math.hpp math/matrix.hpp math/fraction.hpp @@ -99,35 +88,28 @@ set(HEADERS math/statistics.hpp math/numerical.hpp math/gpu_math.hpp - compression/huffman.hpp compression/matrix_compress.hpp - signal/convolve.hpp - optimization/annealing.hpp optimization/pathfinding.hpp - encoding/base.hpp - graphics/flood.hpp graphics/perlin.hpp graphics/simplex.hpp graphics/image_ops.hpp - utils/error_calibration.hpp utils/fnmatch.hpp utils/snowflake.hpp utils/weight.hpp - utils/uuid.hpp -) + utils/uuid.hpp) set(LIBS ${ATOM_ALGORITHM_DEPENDS}) # Add OpenSSL to the list of libraries list(APPEND LIBS OpenSSL::SSL OpenSSL::Crypto loguru) if(TBB_FOUND) - list(APPEND LIBS TBB::tbb) + list(APPEND LIBS TBB::tbb) endif() # Create library target @@ -143,6 +125,4 @@ target_link_libraries(atom-algorithm PRIVATE ${LIBS} ${CMAKE_THREAD_LIBS_INIT}) target_include_directories(atom-algorithm PRIVATE ${OPENSSL_INCLUDE_DIR}) # Install headers -install(FILES ${HEADERS} - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/atom/algorithm -) +install(FILES ${HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/atom/algorithm) diff --git a/atom/algorithm/compression/huffman.cpp b/atom/algorithm/compression/huffman.cpp index 3e706690..d15ca5d0 100644 --- a/atom/algorithm/compression/huffman.cpp +++ b/atom/algorithm/compression/huffman.cpp @@ -231,8 +231,8 @@ auto serializeTree(const HuffmanNode* root) -> std::string { /* ------------------------ deserializeTree ------------------------ */ -auto deserializeTree(const std::string& serializedTree, size_t& index) - -> std::shared_ptr { +auto deserializeTree(const std::string& serializedTree, + size_t& index) -> std::shared_ptr { if (index >= serializedTree.size()) { #ifdef ATOM_USE_BOOST throw HuffmanException(boost::str(boost::format( diff --git a/atom/algorithm/compression/matrix_compress.cpp b/atom/algorithm/compression/matrix_compress.cpp index 134fadf7..e0d4845a 100644 --- a/atom/algorithm/compression/matrix_compress.cpp +++ b/atom/algorithm/compression/matrix_compress.cpp @@ -75,8 +75,8 @@ auto MatrixCompressor::compress(const Matrix& matrix) -> CompressedData { } } -auto MatrixCompressor::compressParallel(const Matrix& matrix, i32 thread_count) - -> CompressedData { +auto MatrixCompressor::compressParallel(const Matrix& matrix, + i32 thread_count) -> CompressedData { if (matrix.empty() || matrix[0].empty()) { return {}; } @@ -209,8 +209,8 @@ auto MatrixCompressor::decompress(const CompressedData& compressed, i32 rows, } auto MatrixCompressor::decompressParallel(const CompressedData& compressed, - i32 rows, i32 cols, i32 thread_count) - -> Matrix { + i32 rows, i32 cols, + i32 thread_count) -> Matrix { if (rows <= 0 || cols <= 0) { THROW_MATRIX_DECOMPRESS_EXCEPTION( "Invalid dimensions: rows and cols must be positive"); @@ -481,9 +481,8 @@ auto MatrixCompressor::decompressWithSIMD(const CompressedData& compressed, return matrix; } -auto MatrixCompressor::generateRandomMatrix(i32 rows, i32 cols, - std::string_view charset) - -> Matrix { +auto MatrixCompressor::generateRandomMatrix( + i32 rows, i32 cols, std::string_view charset) -> Matrix { std::random_device randomDevice; std::mt19937 generator(randomDevice()); std::uniform_int_distribution distribution( diff --git a/atom/algorithm/core/algorithm.cpp b/atom/algorithm/core/algorithm.cpp index 7f4cc0a6..a40c9156 100644 --- a/atom/algorithm/core/algorithm.cpp +++ b/atom/algorithm/core/algorithm.cpp @@ -195,8 +195,8 @@ auto KMP::search(std::string_view text) const -> std::vector { return occurrences; } -auto KMP::searchParallel(std::string_view text, size_t chunk_size) const - -> std::vector { +auto KMP::searchParallel(std::string_view text, + size_t chunk_size) const -> std::vector { if (text.empty() || pattern_.empty() || text.length() < pattern_.length()) { return {}; } diff --git a/atom/algorithm/core/algorithm.hpp b/atom/algorithm/core/algorithm.hpp index 0a6aeb45..3becac72 100644 --- a/atom/algorithm/core/algorithm.hpp +++ b/atom/algorithm/core/algorithm.hpp @@ -295,8 +295,8 @@ template { h(e) } -> std::convertible_to; } auto BloomFilter::hash( - const ElementType& element, std::size_t seed) const noexcept - -> std::size_t { + const ElementType& element, + std::size_t seed) const noexcept -> std::size_t { // Combine the element hash with the seed using FNV-1a variation std::size_t hashValue = 0x811C9DC5 + seed; // FNV offset basis + seed std::size_t elementHash = m_hasher_(element); diff --git a/atom/algorithm/core/opencl_utils.cpp b/atom/algorithm/core/opencl_utils.cpp index 0d26c8fc..836d81e7 100644 --- a/atom/algorithm/core/opencl_utils.cpp +++ b/atom/algorithm/core/opencl_utils.cpp @@ -16,101 +16,101 @@ auto Platform::getPlatforms() -> std::vector { if (err != CL_SUCCESS || num_platforms == 0) { return {}; } - + std::vector platforms(num_platforms); err = clGetPlatformIDs(num_platforms, platforms.data(), nullptr); if (err != CL_SUCCESS) { return {}; } - + return platforms; } auto Platform::getDevices(cl_platform_id platform, DeviceType device_type) -> std::vector { cl_uint num_devices; - cl_int err = clGetDeviceIDs(platform, static_cast(device_type), + cl_int err = clGetDeviceIDs(platform, static_cast(device_type), 0, nullptr, &num_devices); if (err != CL_SUCCESS || num_devices == 0) { return {}; } - + std::vector devices(num_devices); - err = clGetDeviceIDs(platform, static_cast(device_type), + err = clGetDeviceIDs(platform, static_cast(device_type), num_devices, devices.data(), nullptr); if (err != CL_SUCCESS) { return {}; } - + return devices; } auto Platform::getDeviceInfo(cl_device_id device) -> DeviceInfo { DeviceInfo info; - + // Get device name usize name_size; clGetDeviceInfo(device, CL_DEVICE_NAME, 0, nullptr, &name_size); std::string name(name_size, '\0'); clGetDeviceInfo(device, CL_DEVICE_NAME, name_size, name.data(), nullptr); info.name = name.c_str(); // Remove null terminator - + // Get vendor usize vendor_size; clGetDeviceInfo(device, CL_DEVICE_VENDOR, 0, nullptr, &vendor_size); std::string vendor(vendor_size, '\0'); clGetDeviceInfo(device, CL_DEVICE_VENDOR, vendor_size, vendor.data(), nullptr); info.vendor = vendor.c_str(); - + // Get version usize version_size; clGetDeviceInfo(device, CL_DEVICE_VERSION, 0, nullptr, &version_size); std::string version(version_size, '\0'); clGetDeviceInfo(device, CL_DEVICE_VERSION, version_size, version.data(), nullptr); info.version = version.c_str(); - + // Get device type cl_device_type type; clGetDeviceInfo(device, CL_DEVICE_TYPE, sizeof(type), &type, nullptr); info.type = static_cast(type); - + // Get compute units cl_uint compute_units; clGetDeviceInfo(device, CL_DEVICE_MAX_COMPUTE_UNITS, sizeof(compute_units), &compute_units, nullptr); info.max_compute_units = compute_units; - + // Get max work group size usize work_group_size; clGetDeviceInfo(device, CL_DEVICE_MAX_WORK_GROUP_SIZE, sizeof(work_group_size), &work_group_size, nullptr); info.max_work_group_size = work_group_size; - + // Get global memory size cl_ulong global_mem_size; clGetDeviceInfo(device, CL_DEVICE_GLOBAL_MEM_SIZE, sizeof(global_mem_size), &global_mem_size, nullptr); info.global_memory_size = global_mem_size; - + // Get local memory size cl_ulong local_mem_size; clGetDeviceInfo(device, CL_DEVICE_LOCAL_MEM_SIZE, sizeof(local_mem_size), &local_mem_size, nullptr); info.local_memory_size = local_mem_size; - + // Check double precision support usize extensions_size; clGetDeviceInfo(device, CL_DEVICE_EXTENSIONS, 0, nullptr, &extensions_size); std::string extensions(extensions_size, '\0'); clGetDeviceInfo(device, CL_DEVICE_EXTENSIONS, extensions_size, extensions.data(), nullptr); info.supports_double = extensions.find("cl_khr_fp64") != std::string::npos; - + return info; } auto Platform::createContext(const std::vector& devices) -> Context { cl_int err; - cl_context context = clCreateContext(nullptr, static_cast(devices.size()), + cl_context context = clCreateContext(nullptr, static_cast(devices.size()), devices.data(), nullptr, nullptr, &err); if (err != CL_SUCCESS) { THROW_RUNTIME_ERROR("Failed to create OpenCL context: error {}", err); } - + return Context(context); } @@ -120,18 +120,18 @@ auto Platform::createCommandQueue(const Context& context, cl_device_id device) - if (err != CL_SUCCESS) { THROW_RUNTIME_ERROR("Failed to create OpenCL command queue: error {}", err); } - + return CommandQueue(queue); } auto Platform::createBuffer(const Context& context, MemoryFlags flags, usize size, void* host_ptr) -> Buffer { cl_int err; - cl_mem buffer = clCreateBuffer(context.get(), static_cast(flags), + cl_mem buffer = clCreateBuffer(context.get(), static_cast(flags), size, host_ptr, &err); if (err != CL_SUCCESS) { THROW_RUNTIME_ERROR("Failed to create OpenCL buffer: error {}", err); } - + return Buffer(buffer); } @@ -141,7 +141,7 @@ auto Platform::buildKernel(const Context& context, const std::string& kernel_name, const std::string& build_options) -> Kernel { cl_int err; - + // Create program from source const char* source_ptr = source.c_str(); usize source_size = source.length(); @@ -149,30 +149,30 @@ auto Platform::buildKernel(const Context& context, if (err != CL_SUCCESS) { THROW_RUNTIME_ERROR("Failed to create OpenCL program: error {}", err); } - + // Build program - err = clBuildProgram(program, static_cast(devices.size()), devices.data(), + err = clBuildProgram(program, static_cast(devices.size()), devices.data(), build_options.empty() ? nullptr : build_options.c_str(), nullptr, nullptr); - + if (err != CL_SUCCESS) { // Get build log for debugging usize log_size; clGetProgramBuildInfo(program, devices[0], CL_PROGRAM_BUILD_LOG, 0, nullptr, &log_size); std::string build_log(log_size, '\0'); clGetProgramBuildInfo(program, devices[0], CL_PROGRAM_BUILD_LOG, log_size, build_log.data(), nullptr); - + clReleaseProgram(program); THROW_RUNTIME_ERROR("Failed to build OpenCL program: error {}\nBuild log: {}", err, build_log); } - + // Create kernel cl_kernel kernel = clCreateKernel(program, kernel_name.c_str(), &err); clReleaseProgram(program); // Release program as kernel holds reference - + if (err != CL_SUCCESS) { THROW_RUNTIME_ERROR("Failed to create OpenCL kernel '{}': error {}", kernel_name, err); } - + return Kernel(kernel); } @@ -180,17 +180,17 @@ auto ComputeManager::initialize(DeviceType preferred_type) -> bool { if (initialized_) { return true; } - + try { auto platforms = Platform::getPlatforms(); if (platforms.empty()) { return false; } - + // Try to find a device of the preferred type cl_device_id best_device = nullptr; std::vector context_devices; - + for (auto platform : platforms) { auto devices = Platform::getDevices(platform, preferred_type); if (!devices.empty()) { @@ -199,7 +199,7 @@ auto ComputeManager::initialize(DeviceType preferred_type) -> bool { break; } } - + // If preferred type not found, try any device if (!best_device) { for (auto platform : platforms) { @@ -211,20 +211,20 @@ auto ComputeManager::initialize(DeviceType preferred_type) -> bool { } } } - + if (!best_device) { return false; } - + // Create context and command queue context_ = Platform::createContext(context_devices); queue_ = Platform::createCommandQueue(context_, best_device); device_ = best_device; device_info_ = Platform::getDeviceInfo(best_device); - + initialized_ = true; return true; - + } catch (const std::exception&) { return false; } diff --git a/atom/algorithm/core/opencl_utils.hpp b/atom/algorithm/core/opencl_utils.hpp index f865de0c..48a18487 100644 --- a/atom/algorithm/core/opencl_utils.hpp +++ b/atom/algorithm/core/opencl_utils.hpp @@ -1,24 +1,24 @@ #ifndef ATOM_ALGORITHM_CORE_OPENCL_UTILS_HPP #define ATOM_ALGORITHM_CORE_OPENCL_UTILS_HPP +#include #include #include -#include #include -#include +#include #include "rust_numeric.hpp" // OpenCL availability check #ifdef ATOM_USE_OPENCL - #ifdef __APPLE__ - #include - #else - #include - #endif - #define ATOM_OPENCL_AVAILABLE 1 +#ifdef __APPLE__ +#include +#else +#include +#endif +#define ATOM_OPENCL_AVAILABLE 1 #else - #define ATOM_OPENCL_AVAILABLE 0 +#define ATOM_OPENCL_AVAILABLE 0 #endif namespace atom::algorithm::opencl { @@ -54,18 +54,18 @@ class Context { public: Context() = default; explicit Context(cl_context context) : context_(context) {} - + ~Context() { if (context_) { clReleaseContext(context_); } } - + // Move semantics Context(Context&& other) noexcept : context_(other.context_) { other.context_ = nullptr; } - + Context& operator=(Context&& other) noexcept { if (this != &other) { if (context_) { @@ -76,11 +76,11 @@ class Context { } return *this; } - + // Delete copy semantics Context(const Context&) = delete; Context& operator=(const Context&) = delete; - + [[nodiscard]] cl_context get() const noexcept { return context_; } [[nodiscard]] bool valid() const noexcept { return context_ != nullptr; } @@ -95,18 +95,18 @@ class CommandQueue { public: CommandQueue() = default; explicit CommandQueue(cl_command_queue queue) : queue_(queue) {} - + ~CommandQueue() { if (queue_) { clReleaseCommandQueue(queue_); } } - + // Move semantics CommandQueue(CommandQueue&& other) noexcept : queue_(other.queue_) { other.queue_ = nullptr; } - + CommandQueue& operator=(CommandQueue&& other) noexcept { if (this != &other) { if (queue_) { @@ -117,11 +117,11 @@ class CommandQueue { } return *this; } - + // Delete copy semantics CommandQueue(const CommandQueue&) = delete; CommandQueue& operator=(const CommandQueue&) = delete; - + [[nodiscard]] cl_command_queue get() const noexcept { return queue_; } [[nodiscard]] bool valid() const noexcept { return queue_ != nullptr; } @@ -136,18 +136,18 @@ class Buffer { public: Buffer() = default; explicit Buffer(cl_mem buffer) : buffer_(buffer) {} - + ~Buffer() { if (buffer_) { clReleaseMemObject(buffer_); } } - + // Move semantics Buffer(Buffer&& other) noexcept : buffer_(other.buffer_) { other.buffer_ = nullptr; } - + Buffer& operator=(Buffer&& other) noexcept { if (this != &other) { if (buffer_) { @@ -158,11 +158,11 @@ class Buffer { } return *this; } - + // Delete copy semantics Buffer(const Buffer&) = delete; Buffer& operator=(const Buffer&) = delete; - + [[nodiscard]] cl_mem get() const noexcept { return buffer_; } [[nodiscard]] bool valid() const noexcept { return buffer_ != nullptr; } @@ -177,18 +177,18 @@ class Kernel { public: Kernel() = default; explicit Kernel(cl_kernel kernel) : kernel_(kernel) {} - + ~Kernel() { if (kernel_) { clReleaseKernel(kernel_); } } - + // Move semantics Kernel(Kernel&& other) noexcept : kernel_(other.kernel_) { other.kernel_ = nullptr; } - + Kernel& operator=(Kernel&& other) noexcept { if (this != &other) { if (kernel_) { @@ -199,11 +199,11 @@ class Kernel { } return *this; } - + // Delete copy semantics Kernel(const Kernel&) = delete; Kernel& operator=(const Kernel&) = delete; - + [[nodiscard]] cl_kernel get() const noexcept { return kernel_; } [[nodiscard]] bool valid() const noexcept { return kernel_ != nullptr; } @@ -236,40 +236,41 @@ class Platform { * @return Vector of platform IDs */ [[nodiscard]] static auto getPlatforms() -> std::vector; - + /** * @brief Get devices for a platform * @param platform Platform ID * @param device_type Type of devices to query * @return Vector of device IDs */ - [[nodiscard]] static auto getDevices(cl_platform_id platform, - DeviceType device_type = DeviceType::ALL) - -> std::vector; - + [[nodiscard]] static auto getDevices( + cl_platform_id platform, + DeviceType device_type = DeviceType::ALL) -> std::vector; + /** * @brief Get device information * @param device Device ID * @return Device information structure */ [[nodiscard]] static auto getDeviceInfo(cl_device_id device) -> DeviceInfo; - + /** * @brief Create OpenCL context * @param devices Vector of device IDs * @return Context wrapper */ - [[nodiscard]] static auto createContext(const std::vector& devices) -> Context; - + [[nodiscard]] static auto createContext( + const std::vector& devices) -> Context; + /** * @brief Create command queue * @param context OpenCL context * @param device Device ID * @return CommandQueue wrapper */ - [[nodiscard]] static auto createCommandQueue(const Context& context, - cl_device_id device) -> CommandQueue; - + [[nodiscard]] static auto createCommandQueue( + const Context& context, cl_device_id device) -> CommandQueue; + /** * @brief Create buffer * @param context OpenCL context @@ -278,11 +279,10 @@ class Platform { * @param host_ptr Optional host pointer * @return Buffer wrapper */ - [[nodiscard]] static auto createBuffer(const Context& context, - MemoryFlags flags, - usize size, - void* host_ptr = nullptr) -> Buffer; - + [[nodiscard]] static auto createBuffer(const Context& context, + MemoryFlags flags, usize size, + void* host_ptr = nullptr) -> Buffer; + /** * @brief Build kernel from source * @param context OpenCL context @@ -292,11 +292,10 @@ class Platform { * @param build_options Optional build options * @return Kernel wrapper */ - [[nodiscard]] static auto buildKernel(const Context& context, - const std::vector& devices, - const std::string& source, - const std::string& kernel_name, - const std::string& build_options = "") -> Kernel; + [[nodiscard]] static auto buildKernel( + const Context& context, const std::vector& devices, + const std::string& source, const std::string& kernel_name, + const std::string& build_options = "") -> Kernel; }; /** @@ -309,20 +308,21 @@ class ComputeManager { * @param preferred_type Preferred device type * @return true if initialization succeeded */ - [[nodiscard]] auto initialize(DeviceType preferred_type = DeviceType::GPU) -> bool; - + [[nodiscard]] auto initialize(DeviceType preferred_type = DeviceType::GPU) + -> bool; + /** * @brief Check if OpenCL is available and initialized * @return true if available */ [[nodiscard]] auto isAvailable() const noexcept -> bool; - + /** * @brief Get device information * @return Device information */ [[nodiscard]] auto getDeviceInfo() const -> const DeviceInfo&; - + /** * @brief Execute a simple kernel with automatic buffer management * @param kernel_source OpenCL kernel source code @@ -338,7 +338,7 @@ class ComputeManager { usize global_work_size, usize local_work_size, Args&&... args) -> bool; - + /** * @brief Get singleton instance * @return Reference to singleton instance @@ -347,17 +347,17 @@ class ComputeManager { private: ComputeManager() = default; - + Context context_; CommandQueue queue_; cl_device_id device_ = nullptr; DeviceInfo device_info_; bool initialized_ = false; - + std::unordered_map kernel_cache_; }; -#else // !ATOM_OPENCL_AVAILABLE +#else // !ATOM_OPENCL_AVAILABLE /** * @brief Stub implementations when OpenCL is not available @@ -372,8 +372,8 @@ class ComputeManager { } }; -#endif // ATOM_OPENCL_AVAILABLE +#endif // ATOM_OPENCL_AVAILABLE -} // namespace atom::algorithm::opencl +} // namespace atom::algorithm::opencl -#endif // ATOM_ALGORITHM_CORE_OPENCL_UTILS_HPP +#endif // ATOM_ALGORITHM_CORE_OPENCL_UTILS_HPP diff --git a/atom/algorithm/core/simd_utils.hpp b/atom/algorithm/core/simd_utils.hpp index 715a0ebc..96cdb560 100644 --- a/atom/algorithm/core/simd_utils.hpp +++ b/atom/algorithm/core/simd_utils.hpp @@ -8,31 +8,32 @@ #include "rust_numeric.hpp" // SIMD capability detection -#if defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || defined(_M_IX86) - #define ATOM_SIMD_X86 1 - #if defined(__AVX512F__) - #define ATOM_SIMD_AVX512 1 - #include - #elif defined(__AVX2__) - #define ATOM_SIMD_AVX2 1 - #include - #elif defined(__AVX__) - #define ATOM_SIMD_AVX 1 - #include - #elif defined(__SSE4_2__) - #define ATOM_SIMD_SSE42 1 - #include - #elif defined(__SSE4_1__) - #define ATOM_SIMD_SSE41 1 - #include - #elif defined(__SSE2__) - #define ATOM_SIMD_SSE2 1 - #include - #endif +#if defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || \ + defined(_M_IX86) +#define ATOM_SIMD_X86 1 +#if defined(__AVX512F__) +#define ATOM_SIMD_AVX512 1 +#include +#elif defined(__AVX2__) +#define ATOM_SIMD_AVX2 1 +#include +#elif defined(__AVX__) +#define ATOM_SIMD_AVX 1 +#include +#elif defined(__SSE4_2__) +#define ATOM_SIMD_SSE42 1 +#include +#elif defined(__SSE4_1__) +#define ATOM_SIMD_SSE41 1 +#include +#elif defined(__SSE2__) +#define ATOM_SIMD_SSE2 1 +#include +#endif #elif defined(__ARM_NEON) || defined(__aarch64__) - #define ATOM_SIMD_ARM 1 - #define ATOM_SIMD_NEON 1 - #include +#define ATOM_SIMD_ARM 1 +#define ATOM_SIMD_NEON 1 +#include #endif namespace atom::algorithm::simd { @@ -113,7 +114,7 @@ class MemoryOps { #endif std::memcpy(dest, src, size); } - + /** * @brief SIMD-optimized memory set * @param dest Destination pointer @@ -141,28 +142,29 @@ class MemoryOps { static void copyAVX2(void* dest, const void* src, usize size) noexcept { auto* d = static_cast(dest); const auto* s = static_cast(src); - + usize simd_size = size - (size % 32); for (usize i = 0; i < simd_size; i += 32) { - __m256i data = _mm256_load_si256(reinterpret_cast(s + i)); + __m256i data = + _mm256_load_si256(reinterpret_cast(s + i)); _mm256_store_si256(reinterpret_cast<__m256i*>(d + i), data); } - + // Handle remaining bytes if (size % 32 != 0) { std::memcpy(d + simd_size, s + simd_size, size % 32); } } - + static void setAVX2(void* dest, u8 value, usize size) noexcept { auto* d = static_cast(dest); __m256i val = _mm256_set1_epi8(static_cast(value)); - + usize simd_size = size - (size % 32); for (usize i = 0; i < simd_size; i += 32) { _mm256_store_si256(reinterpret_cast<__m256i*>(d + i), val); } - + // Handle remaining bytes if (size % 32 != 0) { std::memset(d + simd_size, value, size % 32); @@ -174,28 +176,29 @@ class MemoryOps { static void copySSE2(void* dest, const void* src, usize size) noexcept { auto* d = static_cast(dest); const auto* s = static_cast(src); - + usize simd_size = size - (size % 16); for (usize i = 0; i < simd_size; i += 16) { - __m128i data = _mm_load_si128(reinterpret_cast(s + i)); + __m128i data = + _mm_load_si128(reinterpret_cast(s + i)); _mm_store_si128(reinterpret_cast<__m128i*>(d + i), data); } - + // Handle remaining bytes if (size % 16 != 0) { std::memcpy(d + simd_size, s + simd_size, size % 16); } } - + static void setSSE2(void* dest, u8 value, usize size) noexcept { auto* d = static_cast(dest); __m128i val = _mm_set1_epi8(static_cast(value)); - + usize simd_size = size - (size % 16); for (usize i = 0; i < simd_size; i += 16) { _mm_store_si128(reinterpret_cast<__m128i*>(d + i), val); } - + // Handle remaining bytes if (size % 16 != 0) { std::memset(d + simd_size, value, size % 16); @@ -217,9 +220,11 @@ class MathOps { * @param size Number of elements */ template - static void vectorAdd(const T* a, const T* b, T* result, usize size) noexcept { - static_assert(std::is_floating_point_v, "Only floating point types supported"); - + static void vectorAdd(const T* a, const T* b, T* result, + usize size) noexcept { + static_assert(std::is_floating_point_v, + "Only floating point types supported"); + if constexpr (std::is_same_v) { #ifdef ATOM_SIMD_AVX2 vectorAddAVX2(a, b, result, size); @@ -240,7 +245,7 @@ class MathOps { #endif } } - + /** * @brief SIMD-optimized dot product * @param a First vector @@ -250,8 +255,9 @@ class MathOps { */ template static T dotProduct(const T* a, const T* b, usize size) noexcept { - static_assert(std::is_floating_point_v, "Only floating point types supported"); - + static_assert(std::is_floating_point_v, + "Only floating point types supported"); + if constexpr (std::is_same_v) { #ifdef ATOM_SIMD_AVX2 return dotProductAVX2(a, b, size); @@ -275,12 +281,13 @@ class MathOps { private: template - static void vectorAddScalar(const T* a, const T* b, T* result, usize size) noexcept { + static void vectorAddScalar(const T* a, const T* b, T* result, + usize size) noexcept { for (usize i = 0; i < size; ++i) { result[i] = a[i] + b[i]; } } - + template static T dotProductScalar(const T* a, const T* b, usize size) noexcept { T sum = T{0}; @@ -291,7 +298,8 @@ class MathOps { } #ifdef ATOM_SIMD_AVX2 - static void vectorAddAVX2(const f32* a, const f32* b, f32* result, usize size) noexcept { + static void vectorAddAVX2(const f32* a, const f32* b, f32* result, + usize size) noexcept { usize simd_size = size - (size % 8); for (usize i = 0; i < simd_size; i += 8) { __m256 va = _mm256_loadu_ps(a + i); @@ -299,13 +307,13 @@ class MathOps { __m256 vr = _mm256_add_ps(va, vb); _mm256_storeu_ps(result + i, vr); } - + // Handle remaining elements for (usize i = simd_size; i < size; ++i) { result[i] = a[i] + b[i]; } } - + static f32 dotProductAVX2(const f32* a, const f32* b, usize size) noexcept { __m256 sum = _mm256_setzero_ps(); usize simd_size = size - (size % 8); @@ -333,7 +341,8 @@ class MathOps { return result; } - static void vectorAddAVX2_f64(const f64* a, const f64* b, f64* result, usize size) noexcept { + static void vectorAddAVX2_f64(const f64* a, const f64* b, f64* result, + usize size) noexcept { usize simd_size = size - (size % 4); for (usize i = 0; i < simd_size; i += 4) { __m256d va = _mm256_loadu_pd(a + i); @@ -348,7 +357,8 @@ class MathOps { } } - static f64 dotProductAVX2_f64(const f64* a, const f64* b, usize size) noexcept { + static f64 dotProductAVX2_f64(const f64* a, const f64* b, + usize size) noexcept { __m256d sum = _mm256_setzero_pd(); usize simd_size = size - (size % 4); @@ -376,7 +386,8 @@ class MathOps { #endif #ifdef ATOM_SIMD_SSE2 - static void vectorAddSSE2(const f32* a, const f32* b, f32* result, usize size) noexcept { + static void vectorAddSSE2(const f32* a, const f32* b, f32* result, + usize size) noexcept { usize simd_size = size - (size % 4); for (usize i = 0; i < simd_size; i += 4) { __m128 va = _mm_loadu_ps(a + i); @@ -417,7 +428,8 @@ class MathOps { return result; } - static void vectorAddSSE2_f64(const f64* a, const f64* b, f64* result, usize size) noexcept { + static void vectorAddSSE2_f64(const f64* a, const f64* b, f64* result, + usize size) noexcept { usize simd_size = size - (size % 2); for (usize i = 0; i < simd_size; i += 2) { __m128d va = _mm_loadu_pd(a + i); @@ -432,7 +444,8 @@ class MathOps { } } - static f64 dotProductSSE2_f64(const f64* a, const f64* b, usize size) noexcept { + static f64 dotProductSSE2_f64(const f64* a, const f64* b, + usize size) noexcept { __m128d sum = _mm_setzero_pd(); usize simd_size = size - (size % 2); @@ -458,7 +471,8 @@ class MathOps { #endif #ifdef ATOM_SIMD_NEON - static void vectorAddNEON(const f32* a, const f32* b, f32* result, usize size) noexcept { + static void vectorAddNEON(const f32* a, const f32* b, f32* result, + usize size) noexcept { usize simd_size = size - (size % 4); for (usize i = 0; i < simd_size; i += 4) { float32x4_t va = vld1q_f32(a + i); @@ -466,32 +480,32 @@ class MathOps { float32x4_t vr = vaddq_f32(va, vb); vst1q_f32(result + i, vr); } - + // Handle remaining elements for (usize i = simd_size; i < size; ++i) { result[i] = a[i] + b[i]; } } - + static f32 dotProductNEON(const f32* a, const f32* b, usize size) noexcept { float32x4_t sum = vdupq_n_f32(0.0f); usize simd_size = size - (size % 4); - + for (usize i = 0; i < simd_size; i += 4) { float32x4_t va = vld1q_f32(a + i); float32x4_t vb = vld1q_f32(b + i); sum = vmlaq_f32(sum, va, vb); } - + // Horizontal sum float32x2_t sum_pair = vadd_f32(vget_high_f32(sum), vget_low_f32(sum)); f32 result = vget_lane_f32(vpadd_f32(sum_pair, sum_pair), 0); - + // Handle remaining elements for (usize i = simd_size; i < size; ++i) { result += a[i] * b[i]; } - + return result; } #endif @@ -509,7 +523,7 @@ class SIMDCapabilities { return false; #endif } - + static bool hasAVX2() noexcept { #ifdef ATOM_SIMD_AVX2 return true; @@ -517,7 +531,7 @@ class SIMDCapabilities { return false; #endif } - + static bool hasAVX512() noexcept { #ifdef ATOM_SIMD_AVX512 return true; @@ -525,7 +539,7 @@ class SIMDCapabilities { return false; #endif } - + static bool hasNEON() noexcept { #ifdef ATOM_SIMD_NEON return true; @@ -535,6 +549,6 @@ class SIMDCapabilities { } }; -} // namespace atom::algorithm::simd +} // namespace atom::algorithm::simd -#endif // ATOM_ALGORITHM_CORE_SIMD_UTILS_HPP +#endif // ATOM_ALGORITHM_CORE_SIMD_UTILS_HPP diff --git a/atom/algorithm/crypto/md5.hpp b/atom/algorithm/crypto/md5.hpp index 5dceaead..5f71860f 100644 --- a/atom/algorithm/crypto/md5.hpp +++ b/atom/algorithm/crypto/md5.hpp @@ -75,8 +75,8 @@ class MD5 { * @return True if the hash of input matches the expected hash */ template - static auto verify(const StrType& input, const std::string& hash) noexcept - -> bool; + static auto verify(const StrType& input, + const std::string& hash) noexcept -> bool; private: /** @@ -157,8 +157,8 @@ auto MD5::encrypt(const StrType& input) -> std::string { } template -auto MD5::verify(const StrType& input, const std::string& hash) noexcept - -> bool { +auto MD5::verify(const StrType& input, + const std::string& hash) noexcept -> bool { try { spdlog::debug("MD5: Verifying hash match for input"); return encrypt(input) == hash; diff --git a/atom/algorithm/crypto/sha1.cpp b/atom/algorithm/crypto/sha1.cpp index 228affcf..9a979485 100644 --- a/atom/algorithm/crypto/sha1.cpp +++ b/atom/algorithm/crypto/sha1.cpp @@ -355,7 +355,8 @@ auto bytesToHex( } // Explicit template instantiation for test usage -template auto bytesToHex<5>(const std::array& bytes) noexcept -> std::string; +template auto bytesToHex<5>(const std::array& bytes) noexcept + -> std::string; template auto computeHashesInParallel(const Containers&... containers) diff --git a/atom/algorithm/crypto/tea.hpp b/atom/algorithm/crypto/tea.hpp index 91def84b..e9245344 100644 --- a/atom/algorithm/crypto/tea.hpp +++ b/atom/algorithm/crypto/tea.hpp @@ -94,8 +94,8 @@ auto teaDecrypt(u32 &value0, u32 &value1, * @throws TEAException if the input data is too small or the key is invalid. */ template -auto xxteaEncrypt(const Container &inputData, std::span inputKey) - -> std::vector; +auto xxteaEncrypt(const Container &inputData, + std::span inputKey) -> std::vector; /** * @brief Decrypts a container of 32-bit values using the XXTEA algorithm. @@ -108,8 +108,8 @@ auto xxteaEncrypt(const Container &inputData, std::span inputKey) * @throws TEAException if the input data is too small or the key is invalid. */ template -auto xxteaDecrypt(const Container &inputData, std::span inputKey) - -> std::vector; +auto xxteaDecrypt(const Container &inputData, + std::span inputKey) -> std::vector; /** * @brief Encrypts two 32-bit values using the XTEA (Extended TEA) algorithm. @@ -121,8 +121,8 @@ auto xxteaDecrypt(const Container &inputData, std::span inputKey) * @param key A reference to an XTEAKey representing the 128-bit key. * @throws TEAException if the key is invalid. */ -auto xteaEncrypt(u32 &value0, u32 &value1, const XTEAKey &key) noexcept(false) - -> void; +auto xteaEncrypt(u32 &value0, u32 &value1, + const XTEAKey &key) noexcept(false) -> void; /** * @brief Decrypts two 32-bit values using the XTEA (Extended TEA) algorithm. @@ -132,8 +132,8 @@ auto xteaEncrypt(u32 &value0, u32 &value1, const XTEAKey &key) noexcept(false) * @param key A reference to an XTEAKey representing the 128-bit key. * @throws TEAException if the key is invalid. */ -auto xteaDecrypt(u32 &value0, u32 &value1, const XTEAKey &key) noexcept(false) - -> void; +auto xteaDecrypt(u32 &value0, u32 &value1, + const XTEAKey &key) noexcept(false) -> void; /** * @brief Converts a byte array to a vector of 32-bit unsigned integers. @@ -148,7 +148,7 @@ auto xteaDecrypt(u32 &value0, u32 &value1, const XTEAKey &key) noexcept(false) */ template requires std::ranges::contiguous_range && - std::same_as, u8> + std::same_as, u8> auto toUint32Vector(const T &data) -> std::vector; /** @@ -294,8 +294,8 @@ auto toByteArrayImpl(std::span data) -> std::vector; * @throws TEAException if the input data is too small or the key is invalid. */ template -auto xxteaEncrypt(const Container &inputData, std::span inputKey) - -> std::vector { +auto xxteaEncrypt(const Container &inputData, + std::span inputKey) -> std::vector { return xxteaEncryptImpl( std::span{inputData.data(), inputData.size()}, inputKey); } @@ -311,8 +311,8 @@ auto xxteaEncrypt(const Container &inputData, std::span inputKey) * @throws TEAException if the input data is too small or the key is invalid. */ template -auto xxteaDecrypt(const Container &inputData, std::span inputKey) - -> std::vector { +auto xxteaDecrypt(const Container &inputData, + std::span inputKey) -> std::vector { return xxteaDecryptImpl( std::span{inputData.data(), inputData.size()}, inputKey); } @@ -332,8 +332,8 @@ auto xxteaDecrypt(const Container &inputData, std::span inputKey) */ template auto xxteaEncryptParallel(const Container &inputData, - std::span inputKey, usize numThreads) - -> std::vector { + std::span inputKey, + usize numThreads) -> std::vector { return xxteaEncryptParallelImpl( std::span{inputData.data(), inputData.size()}, inputKey, numThreads); @@ -354,8 +354,8 @@ auto xxteaEncryptParallel(const Container &inputData, */ template auto xxteaDecryptParallel(const Container &inputData, - std::span inputKey, usize numThreads) - -> std::vector { + std::span inputKey, + usize numThreads) -> std::vector { return xxteaDecryptParallelImpl( std::span{inputData.data(), inputData.size()}, inputKey, numThreads); @@ -374,7 +374,7 @@ auto xxteaDecryptParallel(const Container &inputData, */ template requires std::ranges::contiguous_range && - std::same_as, u8> + std::same_as, u8> auto toUint32Vector(const T &data) -> std::vector { return toUint32VectorImpl(std::span{data.data(), data.size()}); } diff --git a/atom/algorithm/encoding/base.cpp b/atom/algorithm/encoding/base.cpp index 2c66b578..af48a947 100644 --- a/atom/algorithm/encoding/base.cpp +++ b/atom/algorithm/encoding/base.cpp @@ -300,8 +300,8 @@ void base64EncodeSIMD(std::string_view input, OutputIt dest, // 改进后的Base64解码实现 - 使用atom::type::expected template -auto base64DecodeImpl(std::string_view input, OutputIt dest) noexcept - -> atom::type::expected { +auto base64DecodeImpl(std::string_view input, + OutputIt dest) noexcept -> atom::type::expected { usize outSize = 0; std::array inBlock{}; std::array outBlock{}; @@ -410,8 +410,8 @@ auto base64DecodeImpl(std::string_view input, OutputIt dest) noexcept #ifdef ATOM_USE_SIMD // 完善的SIMD优化Base64解码实现 template -auto base64DecodeSIMD(std::string_view input, OutputIt dest) noexcept - -> atom::type::expected { +auto base64DecodeSIMD(std::string_view input, + OutputIt dest) noexcept -> atom::type::expected { #if defined(__AVX2__) // AVX2实现 // 这里应实现完整的AVX2 Base64解码逻辑 @@ -429,8 +429,8 @@ auto base64DecodeSIMD(std::string_view input, OutputIt dest) noexcept #endif // Base64编码接口 -auto base64Encode(std::string_view input, bool padding) noexcept - -> atom::type::expected { +auto base64Encode(std::string_view input, + bool padding) noexcept -> atom::type::expected { try { std::string output; const usize outSize = ((input.size() + 2) / 3) * 4; @@ -645,7 +645,8 @@ auto decodeBase32(std::string_view encoded_sv) noexcept } // Base16/Hex encoding implementation -auto encodeHex(std::span data, bool uppercase) noexcept -> std::string { +auto encodeHex(std::span data, + bool uppercase) noexcept -> std::string { if (data.empty()) { return {}; } @@ -662,10 +663,12 @@ auto encodeHex(std::span data, bool uppercase) noexcept -> s return result; } -auto decodeHex(std::string_view hex) noexcept -> atom::type::expected> { +auto decodeHex(std::string_view hex) noexcept + -> atom::type::expected> { try { if (hex.size() % 2 != 0) { - return atom::type::make_unexpected("Hex string must have even length"); + return atom::type::make_unexpected( + "Hex string must have even length"); } std::vector result; @@ -676,9 +679,12 @@ auto decodeHex(std::string_view hex) noexcept -> atom::type::expected atom::type::expected { - if (c >= '0' && c <= '9') return c - '0'; - if (c >= 'A' && c <= 'F') return c - 'A' + 10; - if (c >= 'a' && c <= 'f') return c - 'a' + 10; + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; return atom::type::make_unexpected("Invalid hex character"); }; @@ -695,14 +701,16 @@ auto decodeHex(std::string_view hex) noexcept -> atom::type::expected std::string { +auto urlEncode(std::string_view str, + bool encodeSpaceAsPlus) noexcept -> std::string { std::string result; - result.reserve(str.size() * 3); // Worst case: every char needs encoding + result.reserve(str.size() * 3); // Worst case: every char needs encoding const char* hexChars = "0123456789ABCDEF"; @@ -711,8 +719,8 @@ auto urlEncode(std::string_view str, bool encodeSpaceAsPlus) noexcept -> std::st // Unreserved characters (RFC 3986) if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || - (c >= '0' && c <= '9') || c == '-' || c == '.' || - c == '_' || c == '~') { + (c >= '0' && c <= '9') || c == '-' || c == '.' || c == '_' || + c == '~') { result += c; } else if (c == ' ' && encodeSpaceAsPlus) { result += '+'; @@ -726,7 +734,8 @@ auto urlEncode(std::string_view str, bool encodeSpaceAsPlus) noexcept -> std::st return result; } -auto urlDecode(std::string_view str) noexcept -> atom::type::expected { +auto urlDecode(std::string_view str) noexcept + -> atom::type::expected { try { std::string result; result.reserve(str.size()); @@ -734,16 +743,20 @@ auto urlDecode(std::string_view str) noexcept -> atom::type::expected= str.size()) { - return atom::type::make_unexpected("Invalid URL encoding: incomplete percent sequence"); + return atom::type::make_unexpected( + "Invalid URL encoding: incomplete percent sequence"); } char high = str[i + 1]; char low = str[i + 2]; auto hexToNibble = [](char c) -> atom::type::expected { - if (c >= '0' && c <= '9') return c - '0'; - if (c >= 'A' && c <= 'F') return c - 'A' + 10; - if (c >= 'a' && c <= 'f') return c - 'a' + 10; + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; return atom::type::make_unexpected("Invalid hex character"); }; @@ -751,13 +764,15 @@ auto urlDecode(std::string_view str) noexcept -> atom::type::expected((highNibble.value() << 4) | lowNibble.value()); - i += 2; // Skip the two hex digits + result += static_cast((highNibble.value() << 4) | + lowNibble.value()); + i += 2; // Skip the two hex digits } else if (str[i] == '+') { - result += ' '; // Convert '+' to space + result += ' '; // Convert '+' to space } else { result += str[i]; } @@ -766,7 +781,8 @@ auto urlDecode(std::string_view str) noexcept -> atom::type::expected> seeds; std::queue> queue; std::vector> visited( @@ -597,7 +598,8 @@ usize FloodFill::fillParallel( queue.emplace(start_x, start_y); visited[static_cast(start_x)][static_cast(start_y)] = true; - seeds.emplace_back(start_x, start_y); // Add starting point as first seed + seeds.emplace_back(start_x, + start_y); // Add starting point as first seed // Find additional seed points for parallel processing while (!queue.empty() && seeds.size() < config.numThreads) { diff --git a/atom/algorithm/graphics/image_ops.hpp b/atom/algorithm/graphics/image_ops.hpp index 01304f2b..da4526cd 100644 --- a/atom/algorithm/graphics/image_ops.hpp +++ b/atom/algorithm/graphics/image_ops.hpp @@ -3,8 +3,8 @@ #include #include -#include #include +#include #include #include @@ -18,7 +18,7 @@ namespace atom::algorithm { /** * @brief Basic image processing operations - * + * * This class provides fundamental image processing algorithms including: * - Convolution with custom kernels * - Gaussian blur @@ -38,38 +38,41 @@ class ImageOps { * @return Convolved image */ template - [[nodiscard]] static auto convolve(std::span image, i32 width, i32 height, - std::span kernel, i32 kernel_size) -> std::vector { + [[nodiscard]] static auto convolve(std::span image, i32 width, + i32 height, std::span kernel, + i32 kernel_size) -> std::vector { if (kernel_size % 2 == 0) { throw std::invalid_argument("Kernel size must be odd"); } - + std::vector result(image.size()); i32 half_kernel = kernel_size / 2; - + for (i32 y = 0; y < height; ++y) { for (i32 x = 0; x < width; ++x) { f32 sum = 0.0f; - + for (i32 ky = -half_kernel; ky <= half_kernel; ++ky) { for (i32 kx = -half_kernel; kx <= half_kernel; ++kx) { i32 px = std::clamp(x + kx, 0, width - 1); i32 py = std::clamp(y + ky, 0, height - 1); - - i32 kernel_idx = (ky + half_kernel) * kernel_size + (kx + half_kernel); - sum += static_cast(image[py * width + px]) * kernel[kernel_idx]; + + i32 kernel_idx = (ky + half_kernel) * kernel_size + + (kx + half_kernel); + sum += static_cast(image[py * width + px]) * + kernel[kernel_idx]; } } - - result[y * width + x] = static_cast(std::clamp(sum, - static_cast(std::numeric_limits::min()), + + result[y * width + x] = static_cast(std::clamp( + sum, static_cast(std::numeric_limits::min()), static_cast(std::numeric_limits::max()))); } } - + return result; } - + /** * @brief Apply Gaussian blur to an image * @param image Input image data @@ -79,15 +82,17 @@ class ImageOps { * @return Blurred image */ template - [[nodiscard]] static auto gaussianBlur(std::span image, i32 width, i32 height, - f32 sigma) -> std::vector { + [[nodiscard]] static auto gaussianBlur(std::span image, i32 width, + i32 height, + f32 sigma) -> std::vector { // Generate Gaussian kernel - i32 kernel_size = static_cast(std::ceil(6 * sigma)) | 1; // Ensure odd size + i32 kernel_size = + static_cast(std::ceil(6 * sigma)) | 1; // Ensure odd size std::vector kernel(kernel_size * kernel_size); - + f32 sum = 0.0f; i32 half_size = kernel_size / 2; - + for (i32 y = -half_size; y <= half_size; ++y) { for (i32 x = -half_size; x <= half_size; ++x) { f32 value = std::exp(-(x * x + y * y) / (2 * sigma * sigma)); @@ -95,15 +100,15 @@ class ImageOps { sum += value; } } - + // Normalize kernel for (auto& val : kernel) { val /= sum; } - + return convolve(image, width, height, kernel, kernel_size); } - + /** * @brief Apply Sobel edge detection * @param image Input image data @@ -112,36 +117,31 @@ class ImageOps { * @return Edge-detected image */ template - [[nodiscard]] static auto sobelEdgeDetection(std::span image, i32 width, i32 height) -> std::vector { + [[nodiscard]] static auto sobelEdgeDetection(std::span image, + i32 width, + i32 height) -> std::vector { // Sobel X kernel - constexpr std::array sobel_x = { - -1, 0, 1, - -2, 0, 2, - -1, 0, 1 - }; - + constexpr std::array sobel_x = {-1, 0, 1, -2, 0, 2, -1, 0, 1}; + // Sobel Y kernel - constexpr std::array sobel_y = { - -1, -2, -1, - 0, 0, 0, - 1, 2, 1 - }; - + constexpr std::array sobel_y = {-1, -2, -1, 0, 0, 0, 1, 2, 1}; + auto grad_x = convolve(image, width, height, sobel_x, 3); auto grad_y = convolve(image, width, height, sobel_y, 3); - + std::vector result(image.size()); - + for (usize i = 0; i < image.size(); ++i) { - f32 magnitude = std::sqrt(static_cast(grad_x[i] * grad_x[i] + grad_y[i] * grad_y[i])); - result[i] = static_cast(std::clamp(magnitude, - static_cast(std::numeric_limits::min()), + f32 magnitude = std::sqrt(static_cast(grad_x[i] * grad_x[i] + + grad_y[i] * grad_y[i])); + result[i] = static_cast(std::clamp( + magnitude, static_cast(std::numeric_limits::min()), static_cast(std::numeric_limits::max()))); } - + return result; } - + /** * @brief Apply Laplacian edge detection * @param image Input image data @@ -150,16 +150,14 @@ class ImageOps { * @return Edge-detected image */ template - [[nodiscard]] static auto laplacianEdgeDetection(std::span image, i32 width, i32 height) -> std::vector { - constexpr std::array laplacian = { - 0, -1, 0, - -1, 4, -1, - 0, -1, 0 - }; - + [[nodiscard]] static auto laplacianEdgeDetection( + std::span image, i32 width, i32 height) -> std::vector { + constexpr std::array laplacian = {0, -1, 0, -1, 4, + -1, 0, -1, 0}; + return convolve(image, width, height, laplacian, 3); } - + /** * @brief Adjust brightness and contrast * @param image Input image data @@ -168,38 +166,42 @@ class ImageOps { * @return Adjusted image */ template - [[nodiscard]] static auto adjustBrightnessContrast(std::span image, - f32 brightness, f32 contrast) -> std::vector { + [[nodiscard]] static auto adjustBrightnessContrast( + std::span image, f32 brightness, + f32 contrast) -> std::vector { std::vector result(image.size()); - + #ifdef ATOM_USE_SIMD if constexpr (std::same_as) { // SIMD implementation for u8 __m256 brightness_vec = _mm256_set1_ps(brightness); __m256 contrast_vec = _mm256_set1_ps(contrast); - + usize simd_end = (image.size() / 8) * 8; - + for (usize i = 0; i < simd_end; i += 8) { // Load 8 bytes and convert to float - __m128i bytes = _mm_loadl_epi64(reinterpret_cast(&image[i])); + __m128i bytes = _mm_loadl_epi64( + reinterpret_cast(&image[i])); __m256i bytes_256 = _mm256_cvtepu8_epi32(bytes); __m256 floats = _mm256_cvtepi32_ps(bytes_256); - + // Apply brightness and contrast floats = _mm256_fmadd_ps(floats, contrast_vec, brightness_vec); - + // Clamp to [0, 255] and convert back to bytes floats = _mm256_max_ps(floats, _mm256_setzero_ps()); floats = _mm256_min_ps(floats, _mm256_set1_ps(255.0f)); __m256i ints = _mm256_cvtps_epi32(floats); - - // Pack back to bytes (this is simplified - full implementation would need proper packing) + + // Pack back to bytes (this is simplified - full implementation + // would need proper packing) for (i32 j = 0; j < 8; ++j) { - result[i + j] = static_cast(_mm256_extract_epi32(ints, j)); + result[i + j] = + static_cast(_mm256_extract_epi32(ints, j)); } } - + // Handle remaining elements for (usize i = simd_end; i < image.size(); ++i) { f32 value = static_cast(image[i]) * contrast + brightness; @@ -211,15 +213,15 @@ class ImageOps { // Scalar implementation for (usize i = 0; i < image.size(); ++i) { f32 value = static_cast(image[i]) * contrast + brightness; - result[i] = static_cast(std::clamp(value, - static_cast(std::numeric_limits::min()), + result[i] = static_cast(std::clamp( + value, static_cast(std::numeric_limits::min()), static_cast(std::numeric_limits::max()))); } } - + return result; } - + /** * @brief Compute histogram of image intensities * @param image Input image data @@ -227,57 +229,60 @@ class ImageOps { * @return Histogram as vector of counts */ template - [[nodiscard]] static auto computeHistogram(std::span image, i32 bins = 256) -> std::vector { + [[nodiscard]] static auto computeHistogram( + std::span image, i32 bins = 256) -> std::vector { std::vector histogram(bins, 0); - + T min_val = *std::min_element(image.begin(), image.end()); T max_val = *std::max_element(image.begin(), image.end()); - f32 scale = static_cast(bins - 1) / static_cast(max_val - min_val); - + f32 scale = + static_cast(bins - 1) / static_cast(max_val - min_val); + for (T pixel : image) { i32 bin = static_cast((pixel - min_val) * scale); bin = std::clamp(bin, 0, bins - 1); histogram[bin]++; } - + return histogram; } - + /** * @brief Apply histogram equalization * @param image Input image data * @return Equalized image */ template - [[nodiscard]] static auto histogramEqualization(std::span image) -> std::vector { + [[nodiscard]] static auto histogramEqualization(std::span image) + -> std::vector { constexpr i32 LEVELS = 256; auto histogram = computeHistogram(image, LEVELS); - + // Compute cumulative distribution function std::vector cdf(LEVELS); cdf[0] = histogram[0]; for (i32 i = 1; i < LEVELS; ++i) { cdf[i] = cdf[i - 1] + histogram[i]; } - + // Create lookup table std::vector lut(LEVELS); u32 total_pixels = static_cast(image.size()); - + for (i32 i = 0; i < LEVELS; ++i) { lut[i] = static_cast((cdf[i] * (LEVELS - 1)) / total_pixels); } - + // Apply lookup table std::vector result(image.size()); for (usize i = 0; i < image.size(); ++i) { result[i] = lut[image[i]]; } - + return result; } }; -} // namespace atom::algorithm +} // namespace atom::algorithm -#endif // ATOM_ALGORITHM_GRAPHICS_IMAGE_OPS_HPP +#endif // ATOM_ALGORITHM_GRAPHICS_IMAGE_OPS_HPP diff --git a/atom/algorithm/graphics/perlin.hpp b/atom/algorithm/graphics/perlin.hpp index 8b934904..2c6fda72 100644 --- a/atom/algorithm/graphics/perlin.hpp +++ b/atom/algorithm/graphics/perlin.hpp @@ -75,9 +75,8 @@ class PerlinNoise { [[nodiscard]] auto generateNoiseMap( i32 width, i32 height, f64 scale, i32 octaves, f64 persistence, - f64 /*lacunarity*/, - i32 seed = std::default_random_engine::default_seed) const - -> std::vector> { + f64 /*lacunarity*/, i32 seed = std::default_random_engine::default_seed) + const -> std::vector> { std::vector> noiseMap(height, std::vector(width)); std::default_random_engine prng(seed); std::uniform_real_distribution dist(-10000, 10000); diff --git a/atom/algorithm/graphics/simplex.hpp b/atom/algorithm/graphics/simplex.hpp index be4c6762..3789743f 100644 --- a/atom/algorithm/graphics/simplex.hpp +++ b/atom/algorithm/graphics/simplex.hpp @@ -13,7 +13,7 @@ namespace atom::algorithm { /** * @brief Simplex noise generator - an improved version of Perlin noise - * + * * Simplex noise has several advantages over Perlin noise: * - Lower computational complexity (O(n) vs O(n²)) * - Better visual isotropy (no directional artifacts) @@ -30,25 +30,26 @@ class SimplexNoise { // Initialize permutation table perm_.resize(512); std::iota(perm_.begin(), perm_.begin() + 256, 0); - + std::default_random_engine engine(seed); - std::ranges::shuffle(std::span(perm_.begin(), perm_.begin() + 256), engine); - + std::ranges::shuffle(std::span(perm_.begin(), perm_.begin() + 256), + engine); + // Duplicate the permutation table std::ranges::copy(std::span(perm_.begin(), perm_.begin() + 256), perm_.begin() + 256); - + // Initialize gradient table for 2D for (usize i = 0; i < 256; ++i) { grad2_[i] = GRAD2[perm_[i] % 8]; } - + // Initialize gradient table for 3D for (usize i = 0; i < 256; ++i) { grad3_[i] = GRAD3[perm_[i] % 12]; } } - + /** * @brief Generate 2D simplex noise * @param x X coordinate @@ -62,7 +63,7 @@ class SimplexNoise { T s = (x + y) * F2; i32 i = static_cast(std::floor(x + s)); i32 j = static_cast(std::floor(y + s)); - + // Unskew the cell origin back to (x,y) space constexpr T G2 = (T(3) - std::sqrt(T(3))) / T(6); T t = (i + j) * G2; @@ -70,32 +71,35 @@ class SimplexNoise { T Y0 = j - t; T x0 = x - X0; T y0 = y - Y0; - + // Determine which simplex we are in i32 i1, j1; if (x0 > y0) { - i1 = 1; j1 = 0; // Lower triangle, XY order: (0,0)->(1,0)->(1,1) + i1 = 1; + j1 = 0; // Lower triangle, XY order: (0,0)->(1,0)->(1,1) } else { - i1 = 0; j1 = 1; // Upper triangle, YX order: (0,0)->(0,1)->(1,1) + i1 = 0; + j1 = 1; // Upper triangle, YX order: (0,0)->(0,1)->(1,1) } - - // Offsets for second (middle) corner of simplex in (x,y) unskewed coords + + // Offsets for second (middle) corner of simplex in (x,y) unskewed + // coords T x1 = x0 - i1 + G2; T y1 = y0 - j1 + G2; // Offsets for last corner of simplex in (x,y) unskewed coords T x2 = x0 - T(1) + T(2) * G2; T y2 = y0 - T(1) + T(2) * G2; - + // Work out the hashed gradient indices of the three simplex corners i32 ii = i & 255; i32 jj = j & 255; i32 gi0 = perm_[ii + perm_[jj]] % 8; i32 gi1 = perm_[ii + i1 + perm_[jj + j1]] % 8; i32 gi2 = perm_[ii + 1 + perm_[jj + 1]] % 8; - + // Calculate the contribution from the three corners T n0, n1, n2; - + T t0 = T(0.5) - x0 * x0 - y0 * y0; if (t0 < 0) { n0 = 0; @@ -103,7 +107,7 @@ class SimplexNoise { t0 *= t0; n0 = t0 * t0 * dot(GRAD2[gi0], x0, y0); } - + T t1 = T(0.5) - x1 * x1 - y1 * y1; if (t1 < 0) { n1 = 0; @@ -111,7 +115,7 @@ class SimplexNoise { t1 *= t1; n1 = t1 * t1 * dot(GRAD2[gi1], x1, y1); } - + T t2 = T(0.5) - x2 * x2 - y2 * y2; if (t2 < 0) { n2 = 0; @@ -119,11 +123,11 @@ class SimplexNoise { t2 *= t2; n2 = t2 * t2 * dot(GRAD2[gi2], x2, y2); } - + // Add contributions from each corner to get the final noise value return T(70) * (n0 + n1 + n2); } - + /** * @brief Generate 3D simplex noise * @param x X coordinate @@ -139,7 +143,7 @@ class SimplexNoise { i32 i = static_cast(std::floor(x + s)); i32 j = static_cast(std::floor(y + s)); i32 k = static_cast(std::floor(z + s)); - + // Unskew the cell origin back to (x,y,z) space constexpr T G3 = T(1) / T(6); T t = (i + j + k) * G3; @@ -149,27 +153,57 @@ class SimplexNoise { T x0 = x - X0; T y0 = y - Y0; T z0 = z - Z0; - + // Determine which simplex we are in i32 i1, j1, k1, i2, j2, k2; if (x0 >= y0) { if (y0 >= z0) { - i1 = 1; j1 = 0; k1 = 0; i2 = 1; j2 = 1; k2 = 0; + i1 = 1; + j1 = 0; + k1 = 0; + i2 = 1; + j2 = 1; + k2 = 0; } else if (x0 >= z0) { - i1 = 1; j1 = 0; k1 = 0; i2 = 1; j2 = 0; k2 = 1; + i1 = 1; + j1 = 0; + k1 = 0; + i2 = 1; + j2 = 0; + k2 = 1; } else { - i1 = 0; j1 = 0; k1 = 1; i2 = 1; j2 = 0; k2 = 1; + i1 = 0; + j1 = 0; + k1 = 1; + i2 = 1; + j2 = 0; + k2 = 1; } } else { if (y0 < z0) { - i1 = 0; j1 = 0; k1 = 1; i2 = 0; j2 = 1; k2 = 1; + i1 = 0; + j1 = 0; + k1 = 1; + i2 = 0; + j2 = 1; + k2 = 1; } else if (x0 < z0) { - i1 = 0; j1 = 1; k1 = 0; i2 = 0; j2 = 1; k2 = 1; + i1 = 0; + j1 = 1; + k1 = 0; + i2 = 0; + j2 = 1; + k2 = 1; } else { - i1 = 0; j1 = 1; k1 = 0; i2 = 1; j2 = 1; k2 = 0; + i1 = 0; + j1 = 1; + k1 = 0; + i2 = 1; + j2 = 1; + k2 = 0; } } - + // Offsets for second corner of simplex in (x,y,z) coords T x1 = x0 - i1 + G3; T y1 = y0 - j1 + G3; @@ -182,7 +216,7 @@ class SimplexNoise { T x3 = x0 - T(1) + T(3) * G3; T y3 = y0 - T(1) + T(3) * G3; T z3 = z0 - T(1) + T(3) * G3; - + // Work out the hashed gradient indices of the four simplex corners i32 ii = i & 255; i32 jj = j & 255; @@ -191,10 +225,10 @@ class SimplexNoise { i32 gi1 = perm_[ii + i1 + perm_[jj + j1 + perm_[kk + k1]]] % 12; i32 gi2 = perm_[ii + i2 + perm_[jj + j2 + perm_[kk + k2]]] % 12; i32 gi3 = perm_[ii + 1 + perm_[jj + 1 + perm_[kk + 1]]] % 12; - + // Calculate the contribution from the four corners T n0, n1, n2, n3; - + T t0 = T(0.6) - x0 * x0 - y0 * y0 - z0 * z0; if (t0 < 0) { n0 = 0; @@ -202,7 +236,7 @@ class SimplexNoise { t0 *= t0; n0 = t0 * t0 * dot(GRAD3[gi0], x0, y0, z0); } - + T t1 = T(0.6) - x1 * x1 - y1 * y1 - z1 * z1; if (t1 < 0) { n1 = 0; @@ -210,7 +244,7 @@ class SimplexNoise { t1 *= t1; n1 = t1 * t1 * dot(GRAD3[gi1], x1, y1, z1); } - + T t2 = T(0.6) - x2 * x2 - y2 * y2 - z2 * z2; if (t2 < 0) { n2 = 0; @@ -218,7 +252,7 @@ class SimplexNoise { t2 *= t2; n2 = t2 * t2 * dot(GRAD3[gi2], x2, y2, z2); } - + T t3 = T(0.6) - x3 * x3 - y3 * y3 - z3 * z3; if (t3 < 0) { n3 = 0; @@ -226,11 +260,11 @@ class SimplexNoise { t3 *= t3; n3 = t3 * t3 * dot(GRAD3[gi3], x3, y3, z3); } - + // Add contributions from each corner to get the final noise value return T(32) * (n0 + n1 + n2 + n3); } - + /** * @brief Generate fractal noise using multiple octaves * @param x X coordinate @@ -241,19 +275,20 @@ class SimplexNoise { * @return Fractal noise value */ template - [[nodiscard]] auto fractal2D(T x, T y, i32 octaves, T persistence, T lacunarity = T(2)) const noexcept -> T { + [[nodiscard]] auto fractal2D(T x, T y, i32 octaves, T persistence, + T lacunarity = T(2)) const noexcept -> T { T total = 0; T frequency = 1; T amplitude = 1; T maxValue = 0; - + for (i32 i = 0; i < octaves; ++i) { total += noise2D(x * frequency, y * frequency) * amplitude; maxValue += amplitude; amplitude *= persistence; frequency *= lacunarity; } - + return total / maxValue; } @@ -261,31 +296,46 @@ class SimplexNoise { std::vector perm_; std::array, 256> grad2_; std::array, 256> grad3_; - + // 2D gradient vectors - static constexpr std::array, 8> GRAD2 = {{ - {{1, 1}}, {{-1, 1}}, {{1, -1}}, {{-1, -1}}, - {{1, 0}}, {{-1, 0}}, {{0, 1}}, {{0, -1}} - }}; - + static constexpr std::array, 8> GRAD2 = {{{{1, 1}}, + {{-1, 1}}, + {{1, -1}}, + {{-1, -1}}, + {{1, 0}}, + {{-1, 0}}, + {{0, 1}}, + {{0, -1}}}}; + // 3D gradient vectors - static constexpr std::array, 12> GRAD3 = {{ - {{1, 1, 0}}, {{-1, 1, 0}}, {{1, -1, 0}}, {{-1, -1, 0}}, - {{1, 0, 1}}, {{-1, 0, 1}}, {{1, 0, -1}}, {{-1, 0, -1}}, - {{0, 1, 1}}, {{0, -1, 1}}, {{0, 1, -1}}, {{0, -1, -1}} - }}; - + static constexpr std::array, 12> GRAD3 = { + {{{1, 1, 0}}, + {{-1, 1, 0}}, + {{1, -1, 0}}, + {{-1, -1, 0}}, + {{1, 0, 1}}, + {{-1, 0, 1}}, + {{1, 0, -1}}, + {{-1, 0, -1}}, + {{0, 1, 1}}, + {{0, -1, 1}}, + {{0, 1, -1}}, + {{0, -1, -1}}}}; + template - static constexpr auto dot(const std::array& g, T x, T y) noexcept -> T { + static constexpr auto dot(const std::array& g, T x, + T y) noexcept -> T { return static_cast(g[0]) * x + static_cast(g[1]) * y; } - + template - static constexpr auto dot(const std::array& g, T x, T y, T z) noexcept -> T { - return static_cast(g[0]) * x + static_cast(g[1]) * y + static_cast(g[2]) * z; + static constexpr auto dot(const std::array& g, T x, T y, + T z) noexcept -> T { + return static_cast(g[0]) * x + static_cast(g[1]) * y + + static_cast(g[2]) * z; } }; -} // namespace atom::algorithm +} // namespace atom::algorithm -#endif // ATOM_ALGORITHM_GRAPHICS_SIMPLEX_HPP +#endif // ATOM_ALGORITHM_GRAPHICS_SIMPLEX_HPP diff --git a/atom/algorithm/hash/mhash.cpp b/atom/algorithm/hash/mhash.cpp index 96daeafc..d82ffdd3 100644 --- a/atom/algorithm/hash/mhash.cpp +++ b/atom/algorithm/hash/mhash.cpp @@ -295,9 +295,11 @@ void MinHash::initializeOpenCL() noexcept { #endif auto MinHash::generateHashFunction() noexcept -> HashFunction { - // Use standard library random instead of atom::utils::Random to avoid include issues + // Use standard library random instead of atom::utils::Random to avoid + // include issues static thread_local std::mt19937_64 gen(std::random_device{}()); - static thread_local std::uniform_int_distribution dist(1, std::numeric_limits::max() - 1); + static thread_local std::uniform_int_distribution dist( + 1, std::numeric_limits::max() - 1); // Use large prime to improve hash quality constexpr usize LARGE_PRIME = 0xFFFFFFFFFFFFFFC5ULL; // 2^64 - 59 (prime) diff --git a/atom/algorithm/hash/mhash.hpp b/atom/algorithm/hash/mhash.hpp index 923f081c..651a0bd6 100644 --- a/atom/algorithm/hash/mhash.hpp +++ b/atom/algorithm/hash/mhash.hpp @@ -157,8 +157,8 @@ class MinHash { */ template requires Hashable> - [[nodiscard]] auto computeSignature(const Range& set) const noexcept(false) - -> HashSignature { + [[nodiscard]] auto computeSignature(const Range& set) const + noexcept(false) -> HashSignature { if (hash_functions_.empty()) { return {}; } diff --git a/atom/algorithm/math/bignumber.hpp b/atom/algorithm/math/bignumber.hpp index 412ef2c8..b0945cb0 100644 --- a/atom/algorithm/math/bignumber.hpp +++ b/atom/algorithm/math/bignumber.hpp @@ -177,29 +177,29 @@ class BigNumber { */ [[nodiscard]] auto abs() const -> BigNumber; - friend auto operator<<(std::ostream& os, const BigNumber& num) - -> std::ostream&; - friend auto operator+(const BigNumber& b1, const BigNumber& b2) - -> BigNumber { + friend auto operator<<(std::ostream& os, + const BigNumber& num) -> std::ostream&; + friend auto operator+(const BigNumber& b1, + const BigNumber& b2) -> BigNumber { return b1.add(b2); } - friend auto operator-(const BigNumber& b1, const BigNumber& b2) - -> BigNumber { + friend auto operator-(const BigNumber& b1, + const BigNumber& b2) -> BigNumber { return b1.subtract(b2); } - friend auto operator*(const BigNumber& b1, const BigNumber& b2) - -> BigNumber { + friend auto operator*(const BigNumber& b1, + const BigNumber& b2) -> BigNumber { return b1.multiply(b2); } - friend auto operator/(const BigNumber& b1, const BigNumber& b2) - -> BigNumber { + friend auto operator/(const BigNumber& b1, + const BigNumber& b2) -> BigNumber { return b1.divide(b2); } friend auto operator^(const BigNumber& b1, int b2) -> BigNumber { return b1.pow(b2); } - friend auto operator==(const BigNumber& b1, const BigNumber& b2) noexcept - -> bool { + friend auto operator==(const BigNumber& b1, + const BigNumber& b2) noexcept -> bool { return b1.equals(b2); } friend auto operator>(const BigNumber& b1, const BigNumber& b2) -> bool; diff --git a/atom/algorithm/math/fraction.hpp b/atom/algorithm/math/fraction.hpp index 5695648d..782415b2 100644 --- a/atom/algorithm/math/fraction.hpp +++ b/atom/algorithm/math/fraction.hpp @@ -406,8 +406,8 @@ class Fraction { * @param f The fraction to output. * @return Reference to the output stream. */ - friend auto operator<<(std::ostream& os, const Fraction& f) - -> std::ostream&; + friend auto operator<<(std::ostream& os, + const Fraction& f) -> std::ostream&; /** * @brief Inputs the fraction from the input stream. diff --git a/atom/algorithm/math/gpu_math.cpp b/atom/algorithm/math/gpu_math.cpp index 9437091c..5617bf1e 100644 --- a/atom/algorithm/math/gpu_math.cpp +++ b/atom/algorithm/math/gpu_math.cpp @@ -1,8 +1,8 @@ #include "gpu_math.hpp" #include -#include #include +#include #include "../../error/exception.hpp" @@ -14,10 +14,10 @@ auto GPUMath::initialize() -> bool { if (initialized_) { return true; } - + compute_manager_ = &opencl::ComputeManager::getInstance(); initialized_ = compute_manager_->initialize(opencl::DeviceType::GPU); - + return initialized_; } @@ -25,60 +25,67 @@ auto GPUMath::isAvailable() const noexcept -> bool { return initialized_ && compute_manager_ && compute_manager_->isAvailable(); } -auto GPUMath::vectorAdd(const std::vector& a, const std::vector& b) -> std::vector { +auto GPUMath::vectorAdd(const std::vector& a, + const std::vector& b) -> std::vector { if (!isAvailable()) { THROW_RUNTIME_ERROR("GPU acceleration not available"); } - + if (a.size() != b.size()) { THROW_INVALID_ARGUMENT("Vector sizes must match"); } - + return executeVectorOperation(getVectorAddKernel(), "vector_add", a, b); } -auto GPUMath::vectorMultiply(const std::vector& a, const std::vector& b) -> std::vector { +auto GPUMath::vectorMultiply(const std::vector& a, + const std::vector& b) -> std::vector { if (!isAvailable()) { THROW_RUNTIME_ERROR("GPU acceleration not available"); } - + if (a.size() != b.size()) { THROW_INVALID_ARGUMENT("Vector sizes must match"); } - - return executeVectorOperation(getVectorMultiplyKernel(), "vector_multiply", a, b); + + return executeVectorOperation(getVectorMultiplyKernel(), "vector_multiply", + a, b); } -auto GPUMath::dotProduct(const std::vector& a, const std::vector& b) -> f32 { +auto GPUMath::dotProduct(const std::vector& a, + const std::vector& b) -> f32 { if (!isAvailable()) { THROW_RUNTIME_ERROR("GPU acceleration not available"); } - + if (a.size() != b.size()) { THROW_INVALID_ARGUMENT("Vector sizes must match"); } - + // For small vectors, use CPU implementation if (a.size() < 1024) { return std::inner_product(a.begin(), a.end(), b.begin(), 0.0f); } - + // TODO: Implement GPU dot product with reduction return std::inner_product(a.begin(), a.end(), b.begin(), 0.0f); } auto GPUMath::calculateMean(const std::vector& data) -> f32 { if (!isAvailable() || data.empty()) { - return std::accumulate(data.begin(), data.end(), 0.0f) / static_cast(data.size()); + return std::accumulate(data.begin(), data.end(), 0.0f) / + static_cast(data.size()); } - + // For small datasets, use CPU implementation if (data.size() < 1024) { - return std::accumulate(data.begin(), data.end(), 0.0f) / static_cast(data.size()); + return std::accumulate(data.begin(), data.end(), 0.0f) / + static_cast(data.size()); } - + // TODO: Implement GPU reduction for mean calculation - return std::accumulate(data.begin(), data.end(), 0.0f) / static_cast(data.size()); + return std::accumulate(data.begin(), data.end(), 0.0f) / + static_cast(data.size()); } auto GPUMath::getInstance() -> GPUMath& { @@ -86,24 +93,25 @@ auto GPUMath::getInstance() -> GPUMath& { return instance; } -auto GPUMath::executeVectorOperation(const std::string& kernel_source, - const std::string& kernel_name, - const std::vector& a, - const std::vector& b) -> std::vector { +auto GPUMath::executeVectorOperation( + const std::string& kernel_source, const std::string& kernel_name, + const std::vector& a, const std::vector& b) -> std::vector { // This is a simplified implementation - in practice, you would: // 1. Create OpenCL buffers for input and output // 2. Build and execute the kernel // 3. Read back the results - + // For now, fall back to CPU implementation std::vector result(a.size()); - + if (kernel_name == "vector_add") { - std::transform(a.begin(), a.end(), b.begin(), result.begin(), std::plus()); + std::transform(a.begin(), a.end(), b.begin(), result.begin(), + std::plus()); } else if (kernel_name == "vector_multiply") { - std::transform(a.begin(), a.end(), b.begin(), result.begin(), std::multiplies()); + std::transform(a.begin(), a.end(), b.begin(), result.begin(), + std::multiplies()); } - + return result; } @@ -148,17 +156,17 @@ __kernel void dot_product(__global const float* a, int gid = get_global_id(0); int lid = get_local_id(0); int group_size = get_local_size(0); - + // Initialize local memory local_sums[lid] = 0.0f; - + // Compute partial products if (gid < size) { local_sums[lid] = a[gid] * b[gid]; } - + barrier(CLK_LOCAL_MEM_FENCE); - + // Reduction in local memory for (int offset = group_size / 2; offset > 0; offset /= 2) { if (lid < offset) { @@ -166,7 +174,7 @@ __kernel void dot_product(__global const float* a, } barrier(CLK_LOCAL_MEM_FENCE); } - + // Write result for this work group if (lid == 0) { partial_sums[get_group_id(0)] = local_sums[0]; @@ -186,7 +194,7 @@ __kernel void matrix_multiply(__global const float* a, const int cols_b) { int row = get_global_id(0); int col = get_global_id(1); - + if (row < rows_a && col < cols_b) { float sum = 0.0f; for (int k = 0; k < cols_a; k++) { @@ -207,7 +215,7 @@ __kernel void matrix_transpose(__global const float* input, const int cols) { int row = get_global_id(0); int col = get_global_id(1); - + if (row < rows && col < cols) { output[col * rows + row] = input[row * cols + col]; } @@ -222,9 +230,9 @@ __kernel void prime_sieve(__global char* is_prime, const int limit) { int gid = get_global_id(0); int p = 2 + gid; - + if (p * p > limit) return; - + if (is_prime[p]) { for (int i = p * p; i <= limit; i += p) { is_prime[i] = 0; @@ -244,11 +252,11 @@ __kernel void reduction_sum(__global const float* input, int gid = get_global_id(0); int lid = get_local_id(0); int group_size = get_local_size(0); - + // Load data into local memory local_data[lid] = (gid < size) ? input[gid] : 0.0f; barrier(CLK_LOCAL_MEM_FENCE); - + // Reduction in local memory for (int offset = group_size / 2; offset > 0; offset /= 2) { if (lid < offset) { @@ -256,7 +264,7 @@ __kernel void reduction_sum(__global const float* input, } barrier(CLK_LOCAL_MEM_FENCE); } - + // Write result for this work group if (lid == 0) { output[get_group_id(0)] = local_data[0]; @@ -276,16 +284,16 @@ __kernel void variance_kernel(__global const float* data, int gid = get_global_id(0); int lid = get_local_id(0); int group_size = get_local_size(0); - + // Compute squared differences local_data[lid] = 0.0f; if (gid < size) { float diff = data[gid] - mean; local_data[lid] = diff * diff; } - + barrier(CLK_LOCAL_MEM_FENCE); - + // Reduction in local memory for (int offset = group_size / 2; offset > 0; offset /= 2) { if (lid < offset) { @@ -293,7 +301,7 @@ __kernel void variance_kernel(__global const float* data, } barrier(CLK_LOCAL_MEM_FENCE); } - + // Write result for this work group if (lid == 0) { partial_vars[get_group_id(0)] = local_data[0]; @@ -303,30 +311,36 @@ __kernel void variance_kernel(__global const float* data, return kernel; } -#else // !ATOM_OPENCL_AVAILABLE +#else // !ATOM_OPENCL_AVAILABLE // Stub implementations when OpenCL is not available auto GPUMath::initialize() -> bool { return false; } auto GPUMath::isAvailable() const noexcept -> bool { return false; } -auto GPUMath::vectorAdd(const std::vector& a, const std::vector& b) -> std::vector { +auto GPUMath::vectorAdd(const std::vector& a, + const std::vector& b) -> std::vector { std::vector result(a.size()); - std::transform(a.begin(), a.end(), b.begin(), result.begin(), std::plus()); + std::transform(a.begin(), a.end(), b.begin(), result.begin(), + std::plus()); return result; } -auto GPUMath::vectorMultiply(const std::vector& a, const std::vector& b) -> std::vector { +auto GPUMath::vectorMultiply(const std::vector& a, + const std::vector& b) -> std::vector { std::vector result(a.size()); - std::transform(a.begin(), a.end(), b.begin(), result.begin(), std::multiplies()); + std::transform(a.begin(), a.end(), b.begin(), result.begin(), + std::multiplies()); return result; } -auto GPUMath::dotProduct(const std::vector& a, const std::vector& b) -> f32 { +auto GPUMath::dotProduct(const std::vector& a, + const std::vector& b) -> f32 { return std::inner_product(a.begin(), a.end(), b.begin(), 0.0f); } auto GPUMath::calculateMean(const std::vector& data) -> f32 { - return std::accumulate(data.begin(), data.end(), 0.0f) / static_cast(data.size()); + return std::accumulate(data.begin(), data.end(), 0.0f) / + static_cast(data.size()); } auto GPUMath::getInstance() -> GPUMath& { @@ -334,6 +348,6 @@ auto GPUMath::getInstance() -> GPUMath& { return instance; } -#endif // ATOM_OPENCL_AVAILABLE +#endif // ATOM_OPENCL_AVAILABLE -} // namespace atom::algorithm::gpu +} // namespace atom::algorithm::gpu diff --git a/atom/algorithm/math/gpu_math.hpp b/atom/algorithm/math/gpu_math.hpp index 6cb0953f..1d52272e 100644 --- a/atom/algorithm/math/gpu_math.hpp +++ b/atom/algorithm/math/gpu_math.hpp @@ -1,9 +1,9 @@ #ifndef ATOM_ALGORITHM_MATH_GPU_MATH_HPP #define ATOM_ALGORITHM_MATH_GPU_MATH_HPP -#include -#include #include +#include +#include #include "../core/opencl_utils.hpp" #include "../rust_numeric.hpp" @@ -12,7 +12,7 @@ namespace atom::algorithm::gpu { /** * @brief GPU-accelerated mathematical operations using OpenCL - * + * * This class provides GPU acceleration for computationally intensive * mathematical operations including: * - Vector operations (addition, multiplication, dot product) @@ -27,37 +27,41 @@ class GPUMath { * @return true if GPU is available and initialized */ [[nodiscard]] auto initialize() -> bool; - + /** * @brief Check if GPU acceleration is available * @return true if available */ [[nodiscard]] auto isAvailable() const noexcept -> bool; - + /** * @brief GPU-accelerated vector addition * @param a First vector * @param b Second vector * @return Result vector (a + b) */ - [[nodiscard]] auto vectorAdd(const std::vector& a, const std::vector& b) -> std::vector; - + [[nodiscard]] auto vectorAdd(const std::vector& a, + const std::vector& b) -> std::vector; + /** * @brief GPU-accelerated vector multiplication (element-wise) * @param a First vector * @param b Second vector * @return Result vector (a * b element-wise) */ - [[nodiscard]] auto vectorMultiply(const std::vector& a, const std::vector& b) -> std::vector; - + [[nodiscard]] auto vectorMultiply(const std::vector& a, + const std::vector& b) + -> std::vector; + /** * @brief GPU-accelerated dot product * @param a First vector * @param b Second vector * @return Dot product result */ - [[nodiscard]] auto dotProduct(const std::vector& a, const std::vector& b) -> f32; - + [[nodiscard]] auto dotProduct(const std::vector& a, + const std::vector& b) -> f32; + /** * @brief GPU-accelerated matrix multiplication * @param a First matrix (row-major order) @@ -67,9 +71,11 @@ class GPUMath { * @param cols_b Number of columns in matrix B * @return Result matrix (row-major order) */ - [[nodiscard]] auto matrixMultiply(const std::vector& a, const std::vector& b, - usize rows_a, usize cols_a, usize cols_b) -> std::vector; - + [[nodiscard]] auto matrixMultiply(const std::vector& a, + const std::vector& b, usize rows_a, + usize cols_a, + usize cols_b) -> std::vector; + /** * @brief GPU-accelerated matrix transpose * @param matrix Input matrix (row-major order) @@ -77,30 +83,33 @@ class GPUMath { * @param cols Number of columns * @return Transposed matrix (row-major order) */ - [[nodiscard]] auto matrixTranspose(const std::vector& matrix, usize rows, usize cols) -> std::vector; - + [[nodiscard]] auto matrixTranspose(const std::vector& matrix, + usize rows, + usize cols) -> std::vector; + /** * @brief GPU-accelerated prime number sieve * @param limit Upper limit for prime generation * @return Vector of prime numbers up to limit */ [[nodiscard]] auto generatePrimes(u32 limit) -> std::vector; - + /** * @brief GPU-accelerated statistical mean calculation * @param data Input data * @return Mean value */ [[nodiscard]] auto calculateMean(const std::vector& data) -> f32; - + /** * @brief GPU-accelerated variance calculation * @param data Input data * @param mean Pre-calculated mean (optional) * @return Variance value */ - [[nodiscard]] auto calculateVariance(const std::vector& data, f32 mean = 0.0f) -> f32; - + [[nodiscard]] auto calculateVariance(const std::vector& data, + f32 mean = 0.0f) -> f32; + /** * @brief Get singleton instance * @return Reference to singleton instance @@ -109,10 +118,10 @@ class GPUMath { private: GPUMath() = default; - + opencl::ComputeManager* compute_manager_ = nullptr; bool initialized_ = false; - + // OpenCL kernel sources static const std::string vector_add_kernel_; static const std::string vector_multiply_kernel_; @@ -124,26 +133,26 @@ class GPUMath { static const std::string variance_kernel_; // Helper methods - [[nodiscard]] auto executeVectorOperation(const std::string& kernel_source, - const std::string& kernel_name, - const std::vector& a, - const std::vector& b) -> std::vector; + [[nodiscard]] auto executeVectorOperation( + const std::string& kernel_source, const std::string& kernel_name, + const std::vector& a, + const std::vector& b) -> std::vector; [[nodiscard]] auto executeReduction(const std::vector& data, - const std::string& kernel_source, - const std::string& kernel_name) -> f32; + const std::string& kernel_source, + const std::string& kernel_name) -> f32; }; - // Get kernel source strings - [[nodiscard]] static auto getVectorAddKernel() -> const std::string&; - [[nodiscard]] static auto getVectorMultiplyKernel() -> const std::string&; - [[nodiscard]] static auto getDotProductKernel() -> const std::string&; - [[nodiscard]] static auto getMatrixMultiplyKernel() -> const std::string&; - [[nodiscard]] static auto getMatrixTransposeKernel() -> const std::string&; - [[nodiscard]] static auto getPrimeSieveKernel() -> const std::string&; - [[nodiscard]] static auto getReductionKernel() -> const std::string&; - [[nodiscard]] static auto getVarianceKernel() -> const std::string&; +// Get kernel source strings +[[nodiscard]] static auto getVectorAddKernel() -> const std::string&; +[[nodiscard]] static auto getVectorMultiplyKernel() -> const std::string&; +[[nodiscard]] static auto getDotProductKernel() -> const std::string&; +[[nodiscard]] static auto getMatrixMultiplyKernel() -> const std::string&; +[[nodiscard]] static auto getMatrixTransposeKernel() -> const std::string&; +[[nodiscard]] static auto getPrimeSieveKernel() -> const std::string&; +[[nodiscard]] static auto getReductionKernel() -> const std::string&; +[[nodiscard]] static auto getVarianceKernel() -> const std::string&; -} // namespace atom::algorithm::gpu +} // namespace atom::algorithm::gpu -#endif // ATOM_ALGORITHM_MATH_GPU_MATH_HPP +#endif // ATOM_ALGORITHM_MATH_GPU_MATH_HPP diff --git a/atom/algorithm/math/math.cpp b/atom/algorithm/math/math.cpp index 75efda73..31da66cf 100644 --- a/atom/algorithm/math/math.cpp +++ b/atom/algorithm/math/math.cpp @@ -226,11 +226,11 @@ void MathMemoryPool::deallocate(void* ptr, usize size) noexcept { #ifdef ATOM_USE_BOOST std::unique_lock lock(mutex_); if (size <= SMALL_BLOCK_SIZE) { - smallPool.free(static_cast(ptr)); + smallPool.free(static_cast(ptr)); } else if (size <= MEDIUM_BLOCK_SIZE) { - mediumPool.free(static_cast(ptr)); + mediumPool.free(static_cast(ptr)); } else if (size <= LARGE_BLOCK_SIZE) { - largePool.free(static_cast(ptr)); + largePool.free(static_cast(ptr)); } else { ::operator delete(ptr); } diff --git a/atom/algorithm/math/math.hpp b/atom/algorithm/math/math.hpp index bce48bea..60e37036 100644 --- a/atom/algorithm/math/math.hpp +++ b/atom/algorithm/math/math.hpp @@ -456,8 +456,8 @@ template * @return std::optional Random value in range, or nullopt if * generation failed */ -[[nodiscard]] auto randomInRange(u64 min, u64 max) noexcept - -> std::optional; +[[nodiscard]] auto randomInRange(u64 min, + u64 max) noexcept -> std::optional; /** * @brief Custom memory pool for efficient allocation in math operations diff --git a/atom/algorithm/math/numerical.hpp b/atom/algorithm/math/numerical.hpp index 4bafee69..5cc14e30 100644 --- a/atom/algorithm/math/numerical.hpp +++ b/atom/algorithm/math/numerical.hpp @@ -14,7 +14,7 @@ namespace atom::algorithm { /** * @brief Numerical methods for solving equations and optimization - * + * * This class provides common numerical algorithms including: * - Root finding (Newton-Raphson, bisection, secant method) * - Numerical integration (trapezoidal, Simpson's rule) @@ -26,7 +26,7 @@ class NumericalMethods { public: using Function = std::function; using Function2D = std::function; - + /** * @brief Find root using Newton-Raphson method * @param f Function to find root of @@ -36,31 +36,32 @@ class NumericalMethods { * @param max_iterations Maximum number of iterations * @return Root if found, nullopt otherwise */ - [[nodiscard]] static auto newtonRaphson(const Function& f, const Function& df, - T initial_guess, T tolerance = T{1e-10}, - usize max_iterations = 100) -> std::optional { + [[nodiscard]] static auto newtonRaphson( + const Function& f, const Function& df, T initial_guess, + T tolerance = T{1e-10}, + usize max_iterations = 100) -> std::optional { T x = initial_guess; - + for (usize i = 0; i < max_iterations; ++i) { T fx = f(x); T dfx = df(x); - + if (std::abs(dfx) < std::numeric_limits::epsilon()) { - return std::nullopt; // Derivative too small + return std::nullopt; // Derivative too small } - + T x_new = x - fx / dfx; - + if (std::abs(x_new - x) < tolerance) { return x_new; } - + x = x_new; } - - return std::nullopt; // Did not converge + + return std::nullopt; // Did not converge } - + /** * @brief Find root using bisection method * @param f Function to find root of @@ -70,25 +71,25 @@ class NumericalMethods { * @param max_iterations Maximum number of iterations * @return Root if found, nullopt otherwise */ - [[nodiscard]] static auto bisection(const Function& f, T a, T b, - T tolerance = T{1e-10}, - usize max_iterations = 100) -> std::optional { + [[nodiscard]] static auto bisection( + const Function& f, T a, T b, T tolerance = T{1e-10}, + usize max_iterations = 100) -> std::optional { T fa = f(a); T fb = f(b); - + // Check if root exists in interval if (fa * fb > T{0}) { return std::nullopt; } - + for (usize i = 0; i < max_iterations; ++i) { T c = (a + b) / T{2}; T fc = f(c); - + if (std::abs(fc) < tolerance || (b - a) / T{2} < tolerance) { return c; } - + if (fa * fc < T{0}) { b = c; fb = fc; @@ -97,10 +98,10 @@ class NumericalMethods { fa = fc; } } - - return (a + b) / T{2}; // Return midpoint if max iterations reached + + return (a + b) / T{2}; // Return midpoint if max iterations reached } - + /** * @brief Find root using secant method * @param f Function to find root of @@ -110,32 +111,32 @@ class NumericalMethods { * @param max_iterations Maximum number of iterations * @return Root if found, nullopt otherwise */ - [[nodiscard]] static auto secant(const Function& f, T x0, T x1, - T tolerance = T{1e-10}, - usize max_iterations = 100) -> std::optional { + [[nodiscard]] static auto secant( + const Function& f, T x0, T x1, T tolerance = T{1e-10}, + usize max_iterations = 100) -> std::optional { T f0 = f(x0); T f1 = f(x1); - + for (usize i = 0; i < max_iterations; ++i) { if (std::abs(f1 - f0) < std::numeric_limits::epsilon()) { - return std::nullopt; // Division by zero + return std::nullopt; // Division by zero } - + T x2 = x1 - f1 * (x1 - x0) / (f1 - f0); - + if (std::abs(x2 - x1) < tolerance) { return x2; } - + x0 = x1; f0 = f1; x1 = x2; f1 = f(x2); } - - return std::nullopt; // Did not converge + + return std::nullopt; // Did not converge } - + /** * @brief Numerical integration using trapezoidal rule * @param f Function to integrate @@ -144,22 +145,23 @@ class NumericalMethods { * @param n Number of intervals * @return Approximate integral value */ - [[nodiscard]] static auto trapezoidalRule(const Function& f, T a, T b, usize n) -> T { + [[nodiscard]] static auto trapezoidalRule(const Function& f, T a, T b, + usize n) -> T { if (n == 0) { return T{0}; } - + T h = (b - a) / static_cast(n); T sum = (f(a) + f(b)) / T{2}; - + for (usize i = 1; i < n; ++i) { T x = a + static_cast(i) * h; sum += f(x); } - + return sum * h; } - + /** * @brief Numerical integration using Simpson's rule * @param f Function to integrate @@ -168,29 +170,30 @@ class NumericalMethods { * @param n Number of intervals (must be even) * @return Approximate integral value */ - [[nodiscard]] static auto simpsonsRule(const Function& f, T a, T b, usize n) -> T { + [[nodiscard]] static auto simpsonsRule(const Function& f, T a, T b, + usize n) -> T { if (n == 0 || n % 2 != 0) { - return T{0}; // n must be even + return T{0}; // n must be even } - + T h = (b - a) / static_cast(n); T sum = f(a) + f(b); - + // Add odd-indexed terms (coefficient 4) for (usize i = 1; i < n; i += 2) { T x = a + static_cast(i) * h; sum += T{4} * f(x); } - + // Add even-indexed terms (coefficient 2) for (usize i = 2; i < n; i += 2) { T x = a + static_cast(i) * h; sum += T{2} * f(x); } - + return sum * h / T{3}; } - + /** * @brief Numerical differentiation using central difference * @param f Function to differentiate @@ -198,10 +201,11 @@ class NumericalMethods { * @param h Step size * @return Approximate derivative value */ - [[nodiscard]] static auto centralDifference(const Function& f, T x, T h = T{1e-8}) -> T { + [[nodiscard]] static auto centralDifference(const Function& f, T x, + T h = T{1e-8}) -> T { return (f(x + h) - f(x - h)) / (T{2} * h); } - + /** * @brief Numerical differentiation using forward difference * @param f Function to differentiate @@ -209,10 +213,11 @@ class NumericalMethods { * @param h Step size * @return Approximate derivative value */ - [[nodiscard]] static auto forwardDifference(const Function& f, T x, T h = T{1e-8}) -> T { + [[nodiscard]] static auto forwardDifference(const Function& f, T x, + T h = T{1e-8}) -> T { return (f(x + h) - f(x)) / h; } - + /** * @brief Numerical differentiation using backward difference * @param f Function to differentiate @@ -220,23 +225,25 @@ class NumericalMethods { * @param h Step size * @return Approximate derivative value */ - [[nodiscard]] static auto backwardDifference(const Function& f, T x, T h = T{1e-8}) -> T { + [[nodiscard]] static auto backwardDifference(const Function& f, T x, + T h = T{1e-8}) -> T { return (f(x) - f(x - h)) / h; } - + /** * @brief Solve linear system Ax = b using Gaussian elimination * @param A Coefficient matrix (will be modified) * @param b Right-hand side vector (will be modified) * @return Solution vector if system is solvable, nullopt otherwise */ - [[nodiscard]] static auto gaussianElimination(std::vector>& A, - std::vector& b) -> std::optional> { + [[nodiscard]] static auto gaussianElimination( + std::vector>& A, + std::vector& b) -> std::optional> { usize n = A.size(); if (n == 0 || A[0].size() != n || b.size() != n) { return std::nullopt; } - + // Forward elimination for (usize i = 0; i < n; ++i) { // Find pivot @@ -246,18 +253,18 @@ class NumericalMethods { max_row = k; } } - + // Swap rows if (max_row != i) { std::swap(A[i], A[max_row]); std::swap(b[i], b[max_row]); } - + // Check for singular matrix if (std::abs(A[i][i]) < std::numeric_limits::epsilon()) { return std::nullopt; } - + // Eliminate column for (usize k = i + 1; k < n; ++k) { T factor = A[k][i] / A[i][i]; @@ -267,7 +274,7 @@ class NumericalMethods { b[k] -= factor * b[i]; } } - + // Back substitution std::vector x(n); for (i64 i = static_cast(n) - 1; i >= 0; --i) { @@ -277,10 +284,10 @@ class NumericalMethods { } x[i] /= A[i][i]; } - + return x; } - + /** * @brief Find minimum using golden section search * @param f Function to minimize @@ -291,14 +298,14 @@ class NumericalMethods { */ [[nodiscard]] static auto goldenSectionSearch(const Function& f, T a, T b, T tolerance = T{1e-10}) -> T { - constexpr T phi = T{1.618033988749895}; // Golden ratio + constexpr T phi = T{1.618033988749895}; // Golden ratio constexpr T resphi = T{2} - phi; - + T x1 = a + resphi * (b - a); T x2 = b - resphi * (b - a); T f1 = f(x1); T f2 = f(x2); - + while (std::abs(b - a) > tolerance) { if (f1 < f2) { b = x2; @@ -314,7 +321,7 @@ class NumericalMethods { f2 = f(x2); } } - + return (a + b) / T{2}; } }; @@ -323,6 +330,6 @@ class NumericalMethods { using NumericalMethodsF = NumericalMethods; using NumericalMethodsD = NumericalMethods; -} // namespace atom::algorithm +} // namespace atom::algorithm -#endif // ATOM_ALGORITHM_MATH_NUMERICAL_HPP +#endif // ATOM_ALGORITHM_MATH_NUMERICAL_HPP diff --git a/atom/algorithm/math/statistics.hpp b/atom/algorithm/math/statistics.hpp index 8be518b2..8139601d 100644 --- a/atom/algorithm/math/statistics.hpp +++ b/atom/algorithm/math/statistics.hpp @@ -13,7 +13,7 @@ namespace atom::algorithm { /** * @brief Statistical functions and utilities - * + * * This class provides common statistical operations including: * - Descriptive statistics (mean, median, mode, variance, etc.) * - Correlation and covariance @@ -32,9 +32,10 @@ class Statistics { if (data.empty()) { return T{0}; } - return std::accumulate(data.begin(), data.end(), T{0}) / static_cast(data.size()); + return std::accumulate(data.begin(), data.end(), T{0}) / + static_cast(data.size()); } - + /** * @brief Calculate the median of a dataset * @param data Input data (will be modified for sorting) @@ -44,17 +45,17 @@ class Statistics { if (data.empty()) { return T{0}; } - + std::sort(data.begin(), data.end()); usize n = data.size(); - + if (n % 2 == 0) { - return (data[n/2 - 1] + data[n/2]) / T{2}; + return (data[n / 2 - 1] + data[n / 2]) / T{2}; } else { - return data[n/2]; + return data[n / 2]; } } - + /** * @brief Calculate the mode(s) of a dataset * @param data Input data @@ -64,58 +65,60 @@ class Statistics { if (data.empty()) { return {}; } - + std::unordered_map frequency; for (T value : data) { frequency[value]++; } - + usize max_freq = 0; for (const auto& [value, freq] : frequency) { max_freq = std::max(max_freq, freq); } - + std::vector modes; for (const auto& [value, freq] : frequency) { if (freq == max_freq) { modes.push_back(value); } } - + return modes; } - + /** * @brief Calculate the sample variance * @param data Input data - * @param sample_correction Whether to use sample correction (n-1 denominator) + * @param sample_correction Whether to use sample correction (n-1 + * denominator) * @return Sample variance */ - [[nodiscard]] static auto variance(std::span data, bool sample_correction = true) -> T { + [[nodiscard]] static auto variance(std::span data, + bool sample_correction = true) -> T { if (data.size() <= 1) { return T{0}; } - + T mean_val = mean(data); T sum_sq_diff = std::transform_reduce( data.begin(), data.end(), T{0}, std::plus{}, - [mean_val](T x) { return (x - mean_val) * (x - mean_val); } - ); - + [mean_val](T x) { return (x - mean_val) * (x - mean_val); }); + usize denominator = sample_correction ? data.size() - 1 : data.size(); return sum_sq_diff / static_cast(denominator); } - + /** * @brief Calculate the standard deviation * @param data Input data * @param sample_correction Whether to use sample correction * @return Standard deviation */ - [[nodiscard]] static auto standardDeviation(std::span data, bool sample_correction = true) -> T { + [[nodiscard]] static auto standardDeviation( + std::span data, bool sample_correction = true) -> T { return std::sqrt(variance(data, sample_correction)); } - + /** * @brief Calculate the skewness of a dataset * @param data Input data @@ -125,25 +128,24 @@ class Statistics { if (data.size() < 3) { return T{0}; } - + T mean_val = mean(data); T std_dev = standardDeviation(data); - + if (std_dev == T{0}) { return T{0}; } - + T sum_cubed = std::transform_reduce( data.begin(), data.end(), T{0}, std::plus{}, [mean_val, std_dev](T x) { T normalized = (x - mean_val) / std_dev; return normalized * normalized * normalized; - } - ); - + }); + return sum_cubed / static_cast(data.size()); } - + /** * @brief Calculate the kurtosis of a dataset * @param data Input data @@ -153,57 +155,58 @@ class Statistics { if (data.size() < 4) { return T{0}; } - + T mean_val = mean(data); T std_dev = standardDeviation(data); - + if (std_dev == T{0}) { return T{0}; } - - T sum_fourth = std::transform_reduce( - data.begin(), data.end(), T{0}, std::plus{}, - [mean_val, std_dev](T x) { - T normalized = (x - mean_val) / std_dev; - T squared = normalized * normalized; - return squared * squared; - } - ); - - return (sum_fourth / static_cast(data.size())) - T{3}; // Excess kurtosis + + T sum_fourth = + std::transform_reduce(data.begin(), data.end(), T{0}, + std::plus{}, [mean_val, std_dev](T x) { + T normalized = (x - mean_val) / std_dev; + T squared = normalized * normalized; + return squared * squared; + }); + + return (sum_fourth / static_cast(data.size())) - + T{3}; // Excess kurtosis } - + /** * @brief Calculate Pearson correlation coefficient between two datasets * @param x First dataset * @param y Second dataset * @return Correlation coefficient (-1 to 1) */ - [[nodiscard]] static auto correlation(std::span x, std::span y) -> T { + [[nodiscard]] static auto correlation(std::span x, + std::span y) -> T { if (x.size() != y.size() || x.empty()) { return T{0}; } - + T mean_x = mean(x); T mean_y = mean(y); - + T numerator = T{0}; T sum_sq_x = T{0}; T sum_sq_y = T{0}; - + for (usize i = 0; i < x.size(); ++i) { T diff_x = x[i] - mean_x; T diff_y = y[i] - mean_y; - + numerator += diff_x * diff_y; sum_sq_x += diff_x * diff_x; sum_sq_y += diff_y * diff_y; } - + T denominator = std::sqrt(sum_sq_x * sum_sq_y); return (denominator == T{0}) ? T{0} : numerator / denominator; } - + /** * @brief Calculate covariance between two datasets * @param x First dataset @@ -211,56 +214,58 @@ class Statistics { * @param sample_correction Whether to use sample correction * @return Covariance */ - [[nodiscard]] static auto covariance(std::span x, std::span y, - bool sample_correction = true) -> T { + [[nodiscard]] static auto covariance(std::span x, + std::span y, + bool sample_correction = true) -> T { if (x.size() != y.size() || x.empty()) { return T{0}; } - + T mean_x = mean(x); T mean_y = mean(y); - + T sum_products = T{0}; for (usize i = 0; i < x.size(); ++i) { sum_products += (x[i] - mean_x) * (y[i] - mean_y); } - + usize denominator = sample_correction ? x.size() - 1 : x.size(); return sum_products / static_cast(denominator); } - + /** * @brief Calculate percentile of a dataset * @param data Input data (will be modified for sorting) * @param percentile Percentile to calculate (0-100) * @return Percentile value */ - [[nodiscard]] static auto percentile(std::vector data, T percentile) -> T { + [[nodiscard]] static auto percentile(std::vector data, + T percentile) -> T { if (data.empty() || percentile < T{0} || percentile > T{100}) { return T{0}; } - + std::sort(data.begin(), data.end()); - + if (percentile == T{0}) { return data.front(); } if (percentile == T{100}) { return data.back(); } - + T index = (percentile / T{100}) * static_cast(data.size() - 1); usize lower_index = static_cast(std::floor(index)); usize upper_index = static_cast(std::ceil(index)); - + if (lower_index == upper_index) { return data[lower_index]; } - + T weight = index - static_cast(lower_index); return data[lower_index] * (T{1} - weight) + data[upper_index] * weight; } - + /** * @brief Calculate the interquartile range (IQR) * @param data Input data @@ -271,59 +276,62 @@ class Statistics { T q3 = percentile(data, T{75}); return q3 - q1; } - + /** * @brief Detect outliers using the IQR method * @param data Input data * @param multiplier IQR multiplier for outlier detection (default: 1.5) * @return Vector of outlier values */ - [[nodiscard]] static auto detectOutliers(std::vector data, T multiplier = T{1.5}) -> std::vector { + [[nodiscard]] static auto detectOutliers(std::vector data, + T multiplier = T{ + 1.5}) -> std::vector { if (data.size() < 4) { return {}; } - + T q1 = percentile(data, T{25}); T q3 = percentile(data, T{75}); T iqr = q3 - q1; - + T lower_bound = q1 - multiplier * iqr; T upper_bound = q3 + multiplier * iqr; - + std::vector outliers; for (T value : data) { if (value < lower_bound || value > upper_bound) { outliers.push_back(value); } } - + return outliers; } - + /** * @brief Calculate z-scores for a dataset * @param data Input data * @return Vector of z-scores */ - [[nodiscard]] static auto zScores(std::span data) -> std::vector { + [[nodiscard]] static auto zScores(std::span data) + -> std::vector { if (data.empty()) { return {}; } - + T mean_val = mean(data); T std_dev = standardDeviation(data); - + if (std_dev == T{0}) { return std::vector(data.size(), T{0}); } - + std::vector z_scores; z_scores.reserve(data.size()); - + for (T value : data) { z_scores.push_back((value - mean_val) / std_dev); } - + return z_scores; } }; @@ -332,6 +340,6 @@ class Statistics { using StatisticsF = Statistics; using StatisticsD = Statistics; -} // namespace atom::algorithm +} // namespace atom::algorithm -#endif // ATOM_ALGORITHM_MATH_STATISTICS_HPP +#endif // ATOM_ALGORITHM_MATH_STATISTICS_HPP diff --git a/atom/algorithm/optimization/annealing.hpp b/atom/algorithm/optimization/annealing.hpp index 9327592b..07493ea3 100644 --- a/atom/algorithm/optimization/annealing.hpp +++ b/atom/algorithm/optimization/annealing.hpp @@ -287,8 +287,8 @@ SimulatedAnnealing::SimulatedAnnealing( accepted_steps_(other.accepted_steps_.load()), rejected_steps_(other.rejected_steps_.load()), start_time_(other.start_time_), - energy_history_(std::make_unique>>(*other.energy_history_)) { -} + energy_history_(std::make_unique>>( + *other.energy_history_)) {} // Move constructor implementation template @@ -313,14 +313,14 @@ SimulatedAnnealing::SimulatedAnnealing( accepted_steps_(other.accepted_steps_.load()), rejected_steps_(other.rejected_steps_.load()), start_time_(other.start_time_), - energy_history_(std::move(other.energy_history_)) { -} + energy_history_(std::move(other.energy_history_)) {} // Copy assignment operator implementation template requires AnnealingProblem SimulatedAnnealing& -SimulatedAnnealing::operator=(const SimulatedAnnealing& other) { +SimulatedAnnealing::operator=( + const SimulatedAnnealing& other) { if (this != &other) { problem_instance_ = other.problem_instance_; cooling_schedule_ = other.cooling_schedule_; @@ -340,7 +340,8 @@ SimulatedAnnealing::operator=(const SimulatedAnnealin accepted_steps_ = other.accepted_steps_.load(); rejected_steps_ = other.rejected_steps_.load(); start_time_ = other.start_time_; - energy_history_ = std::make_unique>>(*other.energy_history_); + energy_history_ = std::make_unique>>( + *other.energy_history_); } return *this; } @@ -349,7 +350,8 @@ SimulatedAnnealing::operator=(const SimulatedAnnealin template requires AnnealingProblem SimulatedAnnealing& -SimulatedAnnealing::operator=(SimulatedAnnealing&& other) noexcept { +SimulatedAnnealing::operator=( + SimulatedAnnealing&& other) noexcept { if (this != &other) { problem_instance_ = other.problem_instance_; cooling_schedule_ = std::move(other.cooling_schedule_); diff --git a/atom/algorithm/signal/convolve.cpp b/atom/algorithm/signal/convolve.cpp index 7b524cd1..567b2e43 100644 --- a/atom/algorithm/signal/convolve.cpp +++ b/atom/algorithm/signal/convolve.cpp @@ -199,8 +199,8 @@ auto extend2D(const std::vector>& input, usize newRows, // Helper function to extend 2D vectors with proper padding modes template auto pad2D(const std::vector>& input, usize padTop, - usize padBottom, usize padLeft, usize padRight, PaddingMode mode) - -> std::vector> { + usize padBottom, usize padLeft, usize padRight, + PaddingMode mode) -> std::vector> { if (input.empty() || input[0].empty()) { THROW_CONVOLVE_ERROR("Cannot pad empty matrix"); } @@ -312,11 +312,10 @@ auto pad2D(const std::vector>& input, usize padTop, } // Helper function to get output dimensions for convolution -auto getConvolutionOutputDimensions(usize inputHeight, usize inputWidth, - usize kernelHeight, usize kernelWidth, - usize strideY, usize strideX, - PaddingMode paddingMode) - -> std::pair { +auto getConvolutionOutputDimensions( + usize inputHeight, usize inputWidth, usize kernelHeight, usize kernelWidth, + usize strideY, usize strideX, + PaddingMode paddingMode) -> std::pair { if (kernelHeight > inputHeight || kernelWidth > inputWidth) { THROW_CONVOLVE_ERROR( "Kernel dimensions ({},{}) cannot be larger than input dimensions " @@ -390,8 +389,8 @@ auto createCommandQueue(cl_context context) -> CLCmdQueuePtr { return CLCmdQueuePtr(commandQueue); } -auto createProgram(const std::string& source, cl_context context) - -> CLProgramPtr { +auto createProgram(const std::string& source, + cl_context context) -> CLProgramPtr { const char* sourceStr = source.c_str(); cl_int err; cl_program program = @@ -613,8 +612,8 @@ auto deconvolve2DOpenCL(const std::vector>& signal, // Function to convolve a 2D input with a 2D kernel using multithreading or // OpenCL auto convolve2D(const std::vector>& input, - const std::vector>& kernel, i32 numThreads) - -> std::vector> { + const std::vector>& kernel, + i32 numThreads) -> std::vector> { try { // 输入验证 if (input.empty() || input[0].empty()) { @@ -679,12 +678,18 @@ auto convolve2D(const std::vector>& input, // 使用SIMD加速内循环计算 for (usize ki = 0; ki < kernelRows; ++ki) { for (usize kj = 0; kj < kernelCols; ++kj) { - // Access input centered at (i, j) with kernel offset - i32 ii = static_cast(i) + static_cast(ki) - static_cast(halfKernelRows); - i32 jj = static_cast(j) + static_cast(kj) - static_cast(halfKernelCols); + // Access input centered at (i, j) with kernel + // offset + i32 ii = static_cast(i) + + static_cast(ki) - + static_cast(halfKernelRows); + i32 jj = static_cast(j) + + static_cast(kj) - + static_cast(halfKernelCols); if (ii >= 0 && ii < static_cast(inputRows) && jj >= 0 && jj < static_cast(inputCols)) { - sum += input[static_cast(ii)][static_cast(jj)] * + sum += input[static_cast(ii)] + [static_cast(jj)] * kernel[ki][kj]; } } @@ -693,12 +698,18 @@ auto convolve2D(const std::vector>& input, // 标准实现 for (usize ki = 0; ki < kernelRows; ++ki) { for (usize kj = 0; kj < kernelCols; ++kj) { - // Access input centered at (i, j) with kernel offset - i32 ii = static_cast(i) + static_cast(ki) - static_cast(halfKernelRows); - i32 jj = static_cast(j) + static_cast(kj) - static_cast(halfKernelCols); + // Access input centered at (i, j) with kernel + // offset + i32 ii = static_cast(i) + + static_cast(ki) - + static_cast(halfKernelRows); + i32 jj = static_cast(j) + + static_cast(kj) - + static_cast(halfKernelCols); if (ii >= 0 && ii < static_cast(inputRows) && jj >= 0 && jj < static_cast(inputCols)) { - sum += input[static_cast(ii)][static_cast(jj)] * + sum += input[static_cast(ii)] + [static_cast(jj)] * kernel[ki][kj]; } } @@ -739,8 +750,8 @@ auto convolve2D(const std::vector>& input, // Function to deconvolve a 2D input with a 2D kernel using multithreading or // OpenCL auto deconvolve2D(const std::vector>& signal, - const std::vector>& kernel, i32 numThreads) - -> std::vector> { + const std::vector>& kernel, + i32 numThreads) -> std::vector> { try { // 输入验证 if (signal.empty() || signal[0].empty()) { @@ -891,8 +902,8 @@ auto deconvolve2D(const std::vector>& signal, } // 2D Discrete Fourier Transform (2D DFT) -auto dfT2D(const std::vector>& signal, i32 numThreads) - -> std::vector>> { +auto dfT2D(const std::vector>& signal, + i32 numThreads) -> std::vector>> { const usize M = signal.size(); const usize N = signal[0].size(); std::vector>> frequency( @@ -1094,8 +1105,8 @@ auto idfT2D(const std::vector>>& spectrum, } // Function to generate a Gaussian kernel -auto generateGaussianKernel(i32 size, f64 sigma) - -> std::vector> { +auto generateGaussianKernel(i32 size, + f64 sigma) -> std::vector> { std::vector> kernel( static_cast(size), std::vector(static_cast(size))); f64 sum = 0.0; @@ -1195,10 +1206,14 @@ auto applyGaussianFilter(const std::vector>& image, for (i32 m = 0; m < SIMD_WIDTH; ++m) { // Center the kernel at position (i, j+m) - i32 x = I32::clamp(static_cast(i) + static_cast(k) - static_cast(kernelRadius), 0, - static_cast(imageHeight) - 1); - i32 y = I32::clamp(static_cast(j) + static_cast(l) + m - static_cast(kernelRadius), 0, - static_cast(imageWidth) - 1); + i32 x = I32::clamp( + static_cast(i) + static_cast(k) - + static_cast(kernelRadius), + 0, static_cast(imageHeight) - 1); + i32 y = I32::clamp(static_cast(j) + + static_cast(l) + m - + static_cast(kernelRadius), + 0, static_cast(imageWidth) - 1); tempBuffer[m] = image[static_cast(x)][static_cast(y)]; } @@ -1224,10 +1239,14 @@ auto applyGaussianFilter(const std::vector>& image, for (usize k = 0; k < kernelSize; ++k) { for (usize l = 0; l < kernelSize; ++l) { // Center the kernel at position (i, j) - i32 x = I32::clamp(static_cast(i) + static_cast(k) - static_cast(kernelRadius), 0, - static_cast(imageHeight) - 1); - i32 y = I32::clamp(static_cast(j) + static_cast(l) - static_cast(kernelRadius), 0, - static_cast(imageWidth) - 1); + i32 x = + I32::clamp(static_cast(i) + static_cast(k) - + static_cast(kernelRadius), + 0, static_cast(imageHeight) - 1); + i32 y = + I32::clamp(static_cast(j) + static_cast(l) - + static_cast(kernelRadius), + 0, static_cast(imageWidth) - 1); sum += image[static_cast(x)][static_cast(y)] * kernel[k][l]; } diff --git a/atom/algorithm/signal/convolve.hpp b/atom/algorithm/signal/convolve.hpp index fdcb4189..7112d34f 100644 --- a/atom/algorithm/signal/convolve.hpp +++ b/atom/algorithm/signal/convolve.hpp @@ -196,8 +196,8 @@ auto idfT2D( i32 numThreads = static_cast(std::thread::hardware_concurrency())) -> std::vector>; -auto generateGaussianKernel(i32 size, f64 sigma) - -> std::vector>; +auto generateGaussianKernel(i32 size, + f64 sigma) -> std::vector>; auto applyGaussianFilter(const std::vector>& image, const std::vector>& kernel) @@ -361,11 +361,10 @@ auto pad2D(const std::vector>& input, usize padTop, * @param paddingMode Mode for handling boundaries * @return std::pair Output dimensions (height, width) */ -auto getConvolutionOutputDimensions(usize inputHeight, usize inputWidth, - usize kernelHeight, usize kernelWidth, - usize strideY = 1, usize strideX = 1, - PaddingMode paddingMode = PaddingMode::SAME) - -> std::pair; +auto getConvolutionOutputDimensions( + usize inputHeight, usize inputWidth, usize kernelHeight, usize kernelWidth, + usize strideY = 1, usize strideX = 1, + PaddingMode paddingMode = PaddingMode::SAME) -> std::pair; /** * @brief Efficient class for working with convolution in frequency domain @@ -456,8 +455,8 @@ auto Convolution1D::convolve(const std::vector& signal, template auto Convolution1D::deconvolve(const std::vector& signal, - const std::vector& kernel, i32 numThreads) - -> std::vector { + const std::vector& kernel, + i32 numThreads) -> std::vector { // Simple 1D deconvolution implementation using frequency domain // This is a basic implementation for compilation compatibility (void)numThreads; // Suppress unused parameter warning @@ -644,8 +643,8 @@ auto FrequencyDomainConvolution::convolve( // Template function implementations template auto pad2D(const std::vector>& input, usize padTop, - usize padBottom, usize padLeft, usize padRight, PaddingMode mode) - -> std::vector> { + usize padBottom, usize padLeft, usize padRight, + PaddingMode mode) -> std::vector> { if (input.empty()) { return {}; } diff --git a/atom/algorithm/utils/error_calibration.hpp b/atom/algorithm/utils/error_calibration.hpp index 23258d77..df2986d3 100644 --- a/atom/algorithm/utils/error_calibration.hpp +++ b/atom/algorithm/utils/error_calibration.hpp @@ -578,11 +578,10 @@ class ErrorCalibration { * @param confidence_level Confidence level for the interval * @return Pair of lower and upper bounds of the confidence interval */ - auto bootstrapConfidenceInterval(const std::vector& measured, - const std::vector& actual, - i32 n_iterations = 1000, - f64 confidence_level = 0.95) - -> std::pair { + auto bootstrapConfidenceInterval( + const std::vector& measured, const std::vector& actual, + i32 n_iterations = 1000, + f64 confidence_level = 0.95) -> std::pair { if (n_iterations <= 0) { THROW_INVALID_ARGUMENT("Number of iterations must be positive."); } @@ -648,8 +647,8 @@ class ErrorCalibration { * @return Tuple of mean residual, standard deviation, and threshold */ auto outlierDetection(const std::vector& measured, - const std::vector& actual, T threshold = 2.0) - -> std::tuple { + const std::vector& actual, + T threshold = 2.0) -> std::tuple { if (residuals_.empty()) { calculateMetrics(measured, actual); } diff --git a/atom/algorithm/utils/fnmatch.hpp b/atom/algorithm/utils/fnmatch.hpp index a751d0af..3a7cd37d 100644 --- a/atom/algorithm/utils/fnmatch.hpp +++ b/atom/algorithm/utils/fnmatch.hpp @@ -109,8 +109,8 @@ template */ template requires StringLike> -[[nodiscard]] auto filter(const Range& names, Pattern&& pattern, int flags = 0) - -> bool; +[[nodiscard]] auto filter(const Range& names, Pattern&& pattern, + int flags = 0) -> bool; /** * @brief Filters a range of strings based on multiple patterns. @@ -127,7 +127,7 @@ template */ template requires StringLike> && - StringLike> + StringLike> [[nodiscard]] auto filter(const Range& names, const PatternRange& patterns, int flags = 0, bool use_parallel = true) -> std::vector>; @@ -271,7 +271,8 @@ auto translate(Pattern&& pattern, int flags) noexcept case '[': { result += '['; if (++it == pattern_view.end()) { - return atom::type::unexpected(FnmatchError::UnmatchedBracket); + return atom::type::unexpected( + FnmatchError::UnmatchedBracket); } if (*it == '!' || *it == '^') { @@ -280,11 +281,13 @@ auto translate(Pattern&& pattern, int flags) noexcept } if (it == pattern_view.end()) { - return atom::type::unexpected(FnmatchError::UnmatchedBracket); + return atom::type::unexpected( + FnmatchError::UnmatchedBracket); } - // Handle ] as first character in bracket expression (it's literal) - // In ECMAScript regex, ] must be escaped even as first char + // Handle ] as first character in bracket expression (it's + // literal) In ECMAScript regex, ] must be escaped even as + // first char if (*it == ']') { result += "\\]"; ++it; @@ -295,10 +298,12 @@ auto translate(Pattern&& pattern, int flags) noexcept *(it + 1) != ']') { result += *it++; if (it == pattern_view.end()) { - return atom::type::unexpected(FnmatchError::UnmatchedBracket); + return atom::type::unexpected( + FnmatchError::UnmatchedBracket); } // Escape special regex characters inside brackets - // Note: dots are literal inside character classes, so don't escape them + // Note: dots are literal inside character classes, + // so don't escape them if (*it == '+' || *it == '(' || *it == ')' || *it == '{' || *it == '}' || *it == '|' || *it == '$' || *it == '\\') { @@ -307,7 +312,8 @@ auto translate(Pattern&& pattern, int flags) noexcept result += *it; } else { // Escape special regex characters inside brackets - // Note: dots, *, and ? are literal inside character classes, so don't escape them + // Note: dots, *, and ? are literal inside character + // classes, so don't escape them if (*it == '+' || *it == '(' || *it == ')' || *it == '{' || *it == '}' || *it == '|' || *it == '$' || *it == '\\') { @@ -319,7 +325,8 @@ auto translate(Pattern&& pattern, int flags) noexcept } if (it == pattern_view.end()) { - return atom::type::unexpected(FnmatchError::UnmatchedBracket); + return atom::type::unexpected( + FnmatchError::UnmatchedBracket); } result += ']'; @@ -329,12 +336,14 @@ auto translate(Pattern&& pattern, int flags) noexcept case '\\': if ((flags & flags::NOESCAPE) == 0) { if (++it == pattern_view.end()) { - return atom::type::unexpected(FnmatchError::EscapeAtEnd); + return atom::type::unexpected( + FnmatchError::EscapeAtEnd); } // Escape the next character for regex - if (*it == '.' || *it == '*' || *it == '?' || *it == '+' || - *it == '(' || *it == ')' || *it == '{' || *it == '}' || - *it == '|' || *it == '^' || *it == '$' || *it == '[' || + if (*it == '.' || *it == '*' || *it == '?' || + *it == '+' || *it == '(' || *it == ')' || + *it == '{' || *it == '}' || *it == '|' || + *it == '^' || *it == '$' || *it == '[' || *it == ']' || *it == '\\') { result += '\\'; } @@ -351,9 +360,9 @@ auto translate(Pattern&& pattern, int flags) noexcept result += ']'; } else { // Escape special regex characters outside brackets - if (*it == '.' || *it == '+' || *it == '(' || *it == ')' || - *it == '{' || *it == '}' || *it == '|' || *it == '^' || - *it == '$') { + if (*it == '.' || *it == '+' || *it == '(' || + *it == ')' || *it == '{' || *it == '}' || + *it == '|' || *it == '^' || *it == '$') { result += '\\'; } result += *it; @@ -384,13 +393,14 @@ auto filter(const Range& names, Pattern&& pattern, int flags) -> bool { } return false; } catch (const std::exception& e) { - throw FnmatchException(std::string("Filter operation failed: ") + e.what()); + throw FnmatchException(std::string("Filter operation failed: ") + + e.what()); } } template requires StringLike> && - StringLike> + StringLike> auto filter(const Range& names, const PatternRange& patterns, int flags, bool use_parallel) -> std::vector> { @@ -403,7 +413,8 @@ auto filter(const Range& names, const PatternRange& patterns, int flags, try { const auto names_size = std::ranges::distance(names); - result.reserve(std::min(static_cast(names_size), static_cast(128))); + result.reserve(std::min(static_cast(names_size), + static_cast(128))); std::vector pattern_views; pattern_views.reserve(std::ranges::distance(patterns)); @@ -432,14 +443,16 @@ auto filter(const Range& names, const PatternRange& patterns, int flags, } } - // Debug output to see what regex is generated - #ifdef DEBUG_FNMATCH - std::cout << "Pattern: " << pattern_view << " -> Regex: " << result << std::endl; - #endif +// Debug output to see what regex is generated +#ifdef DEBUG_FNMATCH + std::cout << "Pattern: " << pattern_view << " -> Regex: " << result + << std::endl; +#endif return result; } catch (const std::exception& e) { - throw FnmatchException(std::string("Filter operation failed: ") + e.what()); + throw FnmatchException(std::string("Filter operation failed: ") + + e.what()); } } diff --git a/atom/algorithm/utils/snowflake.hpp b/atom/algorithm/utils/snowflake.hpp index b01b4a9e..e0396d21 100644 --- a/atom/algorithm/utils/snowflake.hpp +++ b/atom/algorithm/utils/snowflake.hpp @@ -292,8 +292,8 @@ class Snowflake { if (sequence_ == 0) { // Sequence overflow - wait for next millisecond timestamp = wait_next_millis(last_ts); - // Re-load last_timestamp_ in case it was updated by another thread - // Use the maximum to ensure we never go backwards + // Re-load last_timestamp_ in case it was updated by another + // thread Use the maximum to ensure we never go backwards u64 current_last = last_timestamp_.load(); if (timestamp < current_last) { timestamp = current_last; diff --git a/atom/algorithm/utils/uuid.hpp b/atom/algorithm/utils/uuid.hpp index 7fbdbf4f..95fb2042 100644 --- a/atom/algorithm/utils/uuid.hpp +++ b/atom/algorithm/utils/uuid.hpp @@ -16,7 +16,7 @@ namespace atom::algorithm { /** * @brief UUID (Universally Unique Identifier) generator and utilities - * + * * This class provides functionality to generate and manipulate UUIDs according * to RFC 4122. It supports multiple UUID versions: * - Version 1: Time-based UUID @@ -29,27 +29,27 @@ class UUID { * @brief UUID data storage (128 bits) */ using Data = std::array; - + /** * @brief UUID version enumeration */ enum class Version : u8 { - TIME_BASED = 1, ///< Time-based UUID - RANDOM = 4, ///< Random UUID - NAME_SHA1 = 5 ///< Name-based UUID using SHA-1 + TIME_BASED = 1, ///< Time-based UUID + RANDOM = 4, ///< Random UUID + NAME_SHA1 = 5 ///< Name-based UUID using SHA-1 }; - + /** * @brief Default constructor - creates a null UUID */ UUID() : data_{} {} - + /** * @brief Construct UUID from raw data * @param data 16-byte array containing UUID data */ explicit UUID(const Data& data) : data_(data) {} - + /** * @brief Construct UUID from string representation * @param uuid_str String in format "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" @@ -59,7 +59,7 @@ class UUID { data_.fill(0); } } - + /** * @brief Generate a random UUID (version 4) * @return New random UUID @@ -68,80 +68,86 @@ class UUID { static thread_local std::random_device rd; static thread_local std::mt19937_64 gen(rd()); static thread_local std::uniform_int_distribution dis; - + UUID uuid; - + // Generate 128 bits of random data u64 high = dis(gen); u64 low = dis(gen); - + std::memcpy(uuid.data_.data(), &high, 8); std::memcpy(uuid.data_.data() + 8, &low, 8); - + // Set version (4) and variant bits uuid.data_[6] = (uuid.data_[6] & 0x0F) | 0x40; // Version 4 uuid.data_[8] = (uuid.data_[8] & 0x3F) | 0x80; // Variant 10 - + return uuid; } - + /** * @brief Generate a time-based UUID (version 1) * @param node_id 6-byte node identifier (MAC address or random) * @return New time-based UUID */ - [[nodiscard]] static auto generateTimeBased(const std::array& node_id) -> UUID { + [[nodiscard]] static auto generateTimeBased( + const std::array& node_id) -> UUID { static thread_local std::random_device rd; static thread_local std::mt19937 gen(rd()); - static thread_local std::uniform_int_distribution clock_seq_dis(0, 0x3FFF); + static thread_local std::uniform_int_distribution clock_seq_dis( + 0, 0x3FFF); static thread_local u16 clock_seq = clock_seq_dis(gen); - + UUID uuid; - - // Get current time in 100-nanosecond intervals since UUID epoch (1582-10-15) + + // Get current time in 100-nanosecond intervals since UUID epoch + // (1582-10-15) auto now = std::chrono::system_clock::now(); auto duration = now.time_since_epoch(); - auto nanos = std::chrono::duration_cast(duration).count(); - + auto nanos = + std::chrono::duration_cast(duration) + .count(); + // UUID epoch is 1582-10-15 00:00:00 UTC // Difference from Unix epoch (1970-01-01) is 122192928000000000 * 100ns constexpr u64 UUID_EPOCH_OFFSET = 122192928000000000ULL; u64 timestamp = (nanos / 100) + UUID_EPOCH_OFFSET; - + // Time low (32 bits) uuid.data_[0] = static_cast(timestamp & 0xFF); uuid.data_[1] = static_cast((timestamp >> 8) & 0xFF); uuid.data_[2] = static_cast((timestamp >> 16) & 0xFF); uuid.data_[3] = static_cast((timestamp >> 24) & 0xFF); - + // Time mid (16 bits) uuid.data_[4] = static_cast((timestamp >> 32) & 0xFF); uuid.data_[5] = static_cast((timestamp >> 40) & 0xFF); - + // Time high and version (16 bits) - // Version 1 goes in upper nibble of byte 6, time_hi_and_version uses 12 bits + // Version 1 goes in upper nibble of byte 6, time_hi_and_version uses 12 + // bits u16 time_hi = static_cast((timestamp >> 48) & 0x0FFF); - uuid.data_[6] = static_cast(((time_hi >> 8) & 0x0F) | 0x10); // Version 1 in upper nibble - uuid.data_[7] = static_cast(time_hi & 0xFF); // Lower 8 bits of time_hi - + uuid.data_[6] = static_cast(((time_hi >> 8) & 0x0F) | + 0x10); // Version 1 in upper nibble + uuid.data_[7] = + static_cast(time_hi & 0xFF); // Lower 8 bits of time_hi + // Clock sequence and variant uuid.data_[8] = static_cast((clock_seq >> 8) | 0x80); // Variant 10 uuid.data_[9] = static_cast(clock_seq & 0xFF); - + // Node ID std::memcpy(uuid.data_.data() + 10, node_id.data(), 6); - + return uuid; } - + /** * @brief Generate a nil (all zeros) UUID * @return Nil UUID */ - [[nodiscard]] static auto generateNil() -> UUID { - return UUID{}; - } - + [[nodiscard]] static auto generateNil() -> UUID { return UUID{}; } + /** * @brief Convert UUID to string representation * @return String in format "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" @@ -149,7 +155,7 @@ class UUID { [[nodiscard]] auto toString() const -> std::string { std::ostringstream oss; oss << std::hex << std::setfill('0'); - + // Format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx for (usize i = 0; i < 16; ++i) { if (i == 4 || i == 6 || i == 8 || i == 10) { @@ -157,10 +163,10 @@ class UUID { } oss << std::setw(2) << static_cast(data_[i]); } - + return oss.str(); } - + /** * @brief Parse UUID from string representation * @param uuid_str String in format "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" @@ -173,8 +179,8 @@ class UUID { } // Check hyphen positions - if (uuid_str[8] != '-' || uuid_str[13] != '-' || - uuid_str[18] != '-' || uuid_str[23] != '-') { + if (uuid_str[8] != '-' || uuid_str[13] != '-' || uuid_str[18] != '-' || + uuid_str[23] != '-') { data_.fill(0); // Set to nil on failure return false; } @@ -207,7 +213,7 @@ class UUID { return true; } - + /** * @brief Get UUID version * @return UUID version @@ -215,44 +221,43 @@ class UUID { [[nodiscard]] auto getVersion() const -> Version { return static_cast((data_[6] & 0xF0) >> 4); } - + /** * @brief Check if UUID is nil (all zeros) * @return true if UUID is nil, false otherwise */ [[nodiscard]] auto isNil() const -> bool { - return std::all_of(data_.begin(), data_.end(), [](u8 b) { return b == 0; }); + return std::all_of(data_.begin(), data_.end(), + [](u8 b) { return b == 0; }); } - + /** * @brief Get raw UUID data * @return Reference to internal data array */ - [[nodiscard]] auto getData() const -> const Data& { - return data_; - } - + [[nodiscard]] auto getData() const -> const Data& { return data_; } + /** * @brief Equality comparison */ [[nodiscard]] auto operator==(const UUID& other) const -> bool { return data_ == other.data_; } - + /** * @brief Inequality comparison */ [[nodiscard]] auto operator!=(const UUID& other) const -> bool { return !(*this == other); } - + /** * @brief Less-than comparison for ordering */ [[nodiscard]] auto operator<(const UUID& other) const -> bool { return data_ < other.data_; } - + /** * @brief Generate a random node ID for time-based UUIDs * @return 6-byte random node ID @@ -261,15 +266,15 @@ class UUID { static thread_local std::random_device rd; static thread_local std::mt19937 gen(rd()); static thread_local std::uniform_int_distribution dis; - + std::array node_id; for (auto& byte : node_id) { byte = dis(gen); } - + // Set multicast bit to indicate this is not a real MAC address node_id[0] |= 0x01; - + return node_id; } @@ -284,19 +289,22 @@ inline auto operator<<(std::ostream& os, const UUID& uuid) -> std::ostream& { return os << uuid.toString(); } -} // namespace atom::algorithm +} // namespace atom::algorithm // Hash specialization for std::unordered_map/set namespace std { template <> struct hash { - auto operator()(const atom::algorithm::UUID& uuid) const noexcept -> size_t { + auto operator()(const atom::algorithm::UUID& uuid) const noexcept + -> size_t { const auto& data = uuid.getData(); - size_t h1 = hash{}(*reinterpret_cast(data.data())); - size_t h2 = hash{}(*reinterpret_cast(data.data() + 8)); + size_t h1 = + hash{}(*reinterpret_cast(data.data())); + size_t h2 = hash{}( + *reinterpret_cast(data.data() + 8)); return h1 ^ (h2 << 1); } }; -} // namespace std +} // namespace std -#endif // ATOM_ALGORITHM_UTILS_UUID_HPP +#endif // ATOM_ALGORITHM_UTILS_UUID_HPP diff --git a/atom/algorithm/utils/weight.hpp b/atom/algorithm/utils/weight.hpp index 6cf39957..4820b325 100644 --- a/atom/algorithm/utils/weight.hpp +++ b/atom/algorithm/utils/weight.hpp @@ -321,8 +321,8 @@ class WeightSelector { * @param n Number of samples to draw * @return Vector of sampled indices */ - [[nodiscard]] auto sample(std::span weights, usize n) const - -> std::vector { + [[nodiscard]] auto sample(std::span weights, + usize n) const -> std::vector { if (weights.empty()) { throw WeightError("Cannot sample from empty weights"); } @@ -393,9 +393,8 @@ class WeightSelector { } private: - [[nodiscard]] auto sampleUniqueRejection(std::span weights, - usize n) const - -> std::vector { + [[nodiscard]] auto sampleUniqueRejection( + std::span weights, usize n) const -> std::vector { std::vector indices(weights.size()); std::iota(indices.begin(), indices.end(), 0); @@ -440,9 +439,8 @@ class WeightSelector { return results; } - [[nodiscard]] auto sampleUniqueShuffle(std::span weights, - usize n) const - -> std::vector { + [[nodiscard]] auto sampleUniqueShuffle( + std::span weights, usize n) const -> std::vector { std::vector indices(weights.size()); std::iota(indices.begin(), indices.end(), 0); diff --git a/atom/async/CMakeLists.txt b/atom/async/CMakeLists.txt index 9de2178b..db9ef71e 100644 --- a/atom/async/CMakeLists.txt +++ b/atom/async/CMakeLists.txt @@ -1,5 +1,8 @@ cmake_minimum_required(VERSION 3.21) -project(atom-async VERSION 1.0.0 LANGUAGES C CXX) +project( + atom-async + VERSION 1.0.0 + LANGUAGES C CXX) # Include standardized module configuration include(${CMAKE_SOURCE_DIR}/cmake/ModuleDependencies.cmake) @@ -8,19 +11,14 @@ include(${CMAKE_SOURCE_DIR}/cmake/ModuleDependencies.cmake) set(SOURCES # Core files core/promise.cpp - # Threading files threading/lock.cpp - # Synchronization files sync/limiter.cpp - # Utility files utils/timer.cpp - # Execution files - execution/async_executor.cpp -) + execution/async_executor.cpp) # Headers set(HEADERS @@ -47,7 +45,6 @@ set(HEADERS threadlocal.hpp timer.hpp trigger.hpp - # Actual implementation headers (in subdirectories) core/async.hpp core/future.hpp @@ -57,31 +54,25 @@ set(HEADERS core/promise_impl.hpp core/promise_utils.hpp core/promise_void_impl.hpp - threading/lock.hpp threading/thread_wrapper.hpp threading/threadlocal.hpp - messaging/eventstack.hpp messaging/message_bus.hpp messaging/message_queue.hpp messaging/queue.hpp - execution/async_executor.hpp execution/packaged_task.hpp execution/parallel.hpp execution/pool.hpp - sync/limiter.hpp sync/safetype.hpp sync/slot.hpp sync/trigger.hpp - utils/daemon.hpp utils/generator.hpp utils/lodash.hpp - utils/timer.hpp -) + utils/timer.hpp) set(LIBS loguru atom-utils ${CMAKE_THREAD_LIBS_INIT}) @@ -95,6 +86,4 @@ atom_configure_module(atom-async) target_link_libraries(atom-async PRIVATE ${LIBS}) # Install headers -install(FILES ${HEADERS} - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/atom/async -) +install(FILES ${HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/atom/async) diff --git a/atom/async/core/promise.hpp b/atom/async/core/promise.hpp index c4d94201..025552d4 100644 --- a/atom/async/core/promise.hpp +++ b/atom/async/core/promise.hpp @@ -110,7 +110,8 @@ class Promise { // Rule of five for proper resource management ~Promise() noexcept { // Ensure cancellation thread is properly cleaned up - if (cancellationThread_.has_value() && cancellationThread_->joinable()) { + if (cancellationThread_.has_value() && + cancellationThread_->joinable()) { cancellationThread_->request_stop(); try { cancellationThread_->join(); @@ -265,7 +266,8 @@ class Promise { // Rule of five for proper resource management ~Promise() noexcept { // Ensure cancellation thread is properly cleaned up - if (cancellationThread_.has_value() && cancellationThread_->joinable()) { + if (cancellationThread_.has_value() && + cancellationThread_->joinable()) { cancellationThread_->request_stop(); try { cancellationThread_->join(); @@ -1272,7 +1274,8 @@ auto whenAll(std::vector>& promises) { } }; - auto state = std::make_shared(promises.size(), std::move(resultPromise)); + auto state = std::make_shared(promises.size(), + std::move(resultPromise)); // Set callback for each promise for (size_t i = 0; i < promises.size(); ++i) { diff --git a/atom/async/execution/async_executor.hpp b/atom/async/execution/async_executor.hpp index ff8184eb..702863d3 100644 --- a/atom/async/execution/async_executor.hpp +++ b/atom/async/execution/async_executor.hpp @@ -51,7 +51,8 @@ Description: Advanced async task executor with thread pooling #include #endif -// Cache line size definition - to avoid false sharing (if not already defined in macro.hpp) +// Cache line size definition - to avoid false sharing (if not already defined +// in macro.hpp) #ifndef ATOM_CACHE_LINE_SIZE #if defined(ATOM_PLATFORM_WINDOWS) #define ATOM_CACHE_LINE_SIZE 64 @@ -375,7 +376,7 @@ class AsyncExecutor { */ template requires std::invocable && - (!std::same_as>) + (!std::same_as>) auto execute(Func&& func, Priority priority = Priority::Normal) -> std::future> { if (!isRunning()) { diff --git a/atom/async/execution/parallel.hpp b/atom/async/execution/parallel.hpp index 843caae8..d2302cb3 100644 --- a/atom/async/execution/parallel.hpp +++ b/atom/async/execution/parallel.hpp @@ -35,8 +35,8 @@ Description: High-performance parallel algorithms library #include "atom/macro.hpp" #if defined(ATOM_PLATFORM_WINDOWS) -#include "../../../cmake/WindowsCompat.hpp" #include +#include "../../../cmake/WindowsCompat.hpp" #elif defined(ATOM_PLATFORM_APPLE) #include #include @@ -303,7 +303,7 @@ class Parallel { return SetThreadPriority(GetCurrentThread(), winPriority) != 0; #elif defined(ATOM_PLATFORM_LINUX) || defined(ATOM_PLATFORM_MACOS) int policy; - struct sched_param param{}; + struct sched_param param {}; if (pthread_getschedparam(pthread_self(), &policy, ¶m) != 0) { return false; @@ -352,10 +352,10 @@ class Parallel { * @param numThreads 线程数量(0 = 硬件支持的线程数) */ template - requires std::invocable< - Function, typename std::iterator_traits::value_type&> || - std::invocable< - Function, typename std::iterator_traits::value_type> + requires std::invocable::value_type&> || + std::invocable::value_type> static void for_each_jthread(Iterator begin, Iterator end, Function func, size_t numThreads = 0) { if (numThreads == 0) { @@ -434,10 +434,10 @@ class Parallel { * @param numThreads Number of threads to use (0 = hardware concurrency) */ template - requires std::invocable< - Function, typename std::iterator_traits::value_type&> || - std::invocable< - Function, typename std::iterator_traits::value_type> + requires std::invocable::value_type&> || + std::invocable::value_type> static void for_each(Iterator begin, Iterator end, Function func, size_t numThreads = 0) { if (numThreads == 0) { @@ -448,7 +448,8 @@ class Parallel { if (range_size == 0) return; - if (range_size <= static_cast(numThreads) || numThreads == 1) { + if (range_size <= static_cast(numThreads) || + numThreads == 1) { // For small ranges, just use std::for_each std::for_each(begin, end, func); return; @@ -491,8 +492,8 @@ class Parallel { * @return Vector of results from applying the function to each element */ template - requires std::invocable< - Function, typename std::iterator_traits::value_type> + requires std::invocable::value_type> static auto map(Iterator begin, Iterator end, Function func, size_t numThreads = 0) -> std::vector - requires std::predicate< - Predicate, typename std::iterator_traits::value_type> + requires std::predicate::value_type> static auto filter(Iterator begin, Iterator end, Predicate pred, size_t numThreads = 0) -> std::vector::value_type> { @@ -697,7 +698,8 @@ class Parallel { if (range_size == 0) return {}; - if (range_size <= static_cast(numThreads * 4) || numThreads == 1) { + if (range_size <= static_cast(numThreads * 4) || + numThreads == 1) { // For small ranges, just filter sequentially std::vector result; for (auto it = begin; it != end; ++it) { diff --git a/atom/async/execution/pool.hpp b/atom/async/execution/pool.hpp index e03299e4..456a3423 100644 --- a/atom/async/execution/pool.hpp +++ b/atom/async/execution/pool.hpp @@ -945,8 +945,7 @@ class ThreadPool { std::forward(largs)...)); } } catch (...) { - promise->setException( - std::current_exception()); + promise->setException(std::current_exception()); } }); @@ -964,8 +963,7 @@ class ThreadPool { promise->setValue(); } else { promise->setValue(std::invoke( - std::forward(func), - std::forward(largs)...)); + std::forward(func), std::forward(largs)...)); } } catch (...) { promise->setException(std::current_exception()); @@ -1280,7 +1278,8 @@ class ThreadPool { AsioContextWrapper() : context_(std::make_unique()) { // Start the work guard to prevent io_context from running out of // work - workGuard_ = std::make_unique>( + workGuard_ = std::make_unique< + asio::executor_work_guard>( context_->get_executor()); } @@ -1300,7 +1299,9 @@ class ThreadPool { private: std::unique_ptr context_; - std::unique_ptr> workGuard_; + std::unique_ptr< + asio::executor_work_guard> + workGuard_; }; /** diff --git a/atom/async/messaging/message_bus.hpp b/atom/async/messaging/message_bus.hpp index e0561654..ba606ec6 100644 --- a/atom/async/messaging/message_bus.hpp +++ b/atom/async/messaging/message_bus.hpp @@ -1023,9 +1023,9 @@ class MessageBus : public std::enable_shared_from_this { * @return A vector of messages. */ template - [[nodiscard]] auto getMessageHistory( - std::string_view name_sv, std::size_t count = K_MAX_HISTORY_SIZE) const - -> std::vector { + [[nodiscard]] auto getMessageHistory(std::string_view name_sv, + std::size_t count = K_MAX_HISTORY_SIZE) + const -> std::vector { try { if (count == 0) { return {}; diff --git a/atom/async/messaging/message_queue.hpp b/atom/async/messaging/message_queue.hpp index 754b472f..548915bf 100644 --- a/atom/async/messaging/message_queue.hpp +++ b/atom/async/messaging/message_queue.hpp @@ -128,8 +128,8 @@ template class MessageQueue; // Note: A previous non-templated MessageAwaiter referencing 'T' was removed -// because it was invalid at namespace scope. Use MessageQueue::MessageAwaitable -// defined below for coroutine support. +// because it was invalid at namespace scope. Use +// MessageQueue::MessageAwaitable defined below for coroutine support. /** * @brief A message queue that allows subscribers to receive messages of type T. @@ -420,7 +420,7 @@ class MessageQueue { if (stoken.stop_requested()) break; - // After wait, re-check queues. Lock is held. + // After wait, re-check queues. Lock is held. #ifdef ATOM_USE_LOCKFREE_QUEUE if (m_lockfreeQueue_.pop( currentMessage)) { // Pop while lock is held diff --git a/atom/async/messaging/queue.hpp b/atom/async/messaging/queue.hpp index 81221fdc..a6e6716f 100644 --- a/atom/async/messaging/queue.hpp +++ b/atom/async/messaging/queue.hpp @@ -712,9 +712,8 @@ class ThreadSafeQueue { * is being destroyed */ template - [[nodiscard]] auto takeUntil( - const std::chrono::time_point& timeout_time) - -> std::optional { + [[nodiscard]] auto takeUntil(const std::chrono::time_point& + timeout_time) -> std::optional { std::unique_lock lock(m_mutex); if (m_conditionVariable_.wait_until(lock, timeout_time, [this] { return !m_queue_.empty() || m_mustReturnNullptr_; diff --git a/atom/async/sync/safetype.hpp b/atom/async/sync/safetype.hpp index 620440b8..f0dd741a 100644 --- a/atom/async/sync/safetype.hpp +++ b/atom/async/sync/safetype.hpp @@ -852,9 +852,9 @@ namespace sync { /** * @brief Thread-safe wrapper for any type T * - * SafeType provides thread-safe access to a value of type T using a shared_mutex - * for reader-writer synchronization. Multiple readers can access the value - * concurrently, but writers have exclusive access. + * SafeType provides thread-safe access to a value of type T using a + * shared_mutex for reader-writer synchronization. Multiple readers can access + * the value concurrently, but writers have exclusive access. * * @tparam T The type to wrap */ diff --git a/atom/async/sync/slot.hpp b/atom/async/sync/slot.hpp index a0c927a6..e7a364d2 100644 --- a/atom/async/sync/slot.hpp +++ b/atom/async/sync/slot.hpp @@ -110,7 +110,8 @@ class Slot { std::optional getWithTimeout( const std::chrono::duration& timeout) { std::unique_lock lock(mutex_); - if (!not_empty_.wait_for(lock, timeout, [&] { return !queue_.empty(); })) { + if (!not_empty_.wait_for(lock, timeout, + [&] { return !queue_.empty(); })) { return std::nullopt; } T value = std::move(queue_.front()); @@ -957,10 +958,9 @@ class ScopedSignal { try { std::lock_guard lock(mutex_); // Remove expired weak_ptr slots - auto it = std::remove_if(slots_.begin(), slots_.end(), - [](const WeakSlotPtr& weakSlot) { - return weakSlot.expired(); - }); + auto it = std::remove_if( + slots_.begin(), slots_.end(), + [](const WeakSlotPtr& weakSlot) { return weakSlot.expired(); }); slots_.erase(it, slots_.end()); for (const auto& weakSlot : slots_) { diff --git a/atom/async/threading/lock.cpp b/atom/async/threading/lock.cpp index 0f9707ba..792a7630 100644 --- a/atom/async/threading/lock.cpp +++ b/atom/async/threading/lock.cpp @@ -318,16 +318,14 @@ auto LockFactory::createLock(LockType type) } auto LockFactory::createOptimizedLock() - -> std::unique_ptr> { + -> std::unique_ptr> { // For now, return a simple mutex as the optimized lock // In a real implementation, this could choose between different lock types // based on platform capabilities and performance characteristics auto mutex = std::make_unique(); - auto deleter = [](void* ptr) { - delete static_cast(ptr); - }; - return std::unique_ptr>( - mutex.release(), deleter); + auto deleter = [](void* ptr) { delete static_cast(ptr); }; + return std::unique_ptr>(mutex.release(), + deleter); } } // namespace atom::async diff --git a/atom/async/threading/thread_wrapper.hpp b/atom/async/threading/thread_wrapper.hpp index cc9c3362..42182b1b 100644 --- a/atom/async/threading/thread_wrapper.hpp +++ b/atom/async/threading/thread_wrapper.hpp @@ -143,7 +143,8 @@ class Thread : public NonCopyable { * @throws ThreadException if the thread cannot be started. */ template - requires (ThreadCallable || StopTokenCallable) + requires(ThreadCallable || + StopTokenCallable) void start(Callable&& func, Args&&... args) { try { // Clean up any existing thread @@ -225,8 +226,8 @@ class Thread : public NonCopyable { */ template requires ThreadCallable - [[nodiscard]] auto startWithResult(Callable&& func, Args&&... args) - -> std::future { + [[nodiscard]] auto startWithResult(Callable&& func, + Args&&... args) -> std::future { auto task = std::make_shared>( [func = std::forward(func), ... args = std::forward(args)]() mutable -> R { @@ -421,9 +422,8 @@ class Thread : public NonCopyable { * @return true if joined successfully, false if timed out. */ template - [[nodiscard]] auto tryJoinFor( - const std::chrono::duration& timeout_duration) noexcept - -> bool { + [[nodiscard]] auto tryJoinFor(const std::chrono::duration& + timeout_duration) noexcept -> bool { if (!running()) { return true; // Thread is not running, so join succeeded } diff --git a/atom/async/threading/threadlocal.hpp b/atom/async/threading/threadlocal.hpp index 8788ec71..80f7de94 100644 --- a/atom/async/threading/threadlocal.hpp +++ b/atom/async/threading/threadlocal.hpp @@ -210,8 +210,8 @@ class EnhancedThreadLocal : public NonCopyable { EnhancedThreadLocal(EnhancedThreadLocal&&) noexcept = default; // Move assignment operator - auto operator=(EnhancedThreadLocal&&) noexcept - -> EnhancedThreadLocal& = default; + auto operator=(EnhancedThreadLocal&&) noexcept -> EnhancedThreadLocal& = + default; /** * @brief Destructor, responsible for cleaning up all thread values @@ -300,7 +300,8 @@ class EnhancedThreadLocal : public NonCopyable { * @brief Get the thread-local value for the current thread (const version) * * @return Const reference to the thread-local value - * @throws ThreadLocalException If the value has not been set for this thread + * @throws ThreadLocalException If the value has not been set for this + * thread */ auto get() const -> const T& { auto tid = std::this_thread::get_id(); @@ -308,8 +309,9 @@ class EnhancedThreadLocal : public NonCopyable { auto it = values_.find(tid); if (it == values_.end() || !it->second.has_value()) { - throw ThreadLocalException(ThreadLocalError::ValueNotFound, - "Thread-local value not set for this thread"); + throw ThreadLocalException( + ThreadLocalError::ValueNotFound, + "Thread-local value not set for this thread"); } return it->second.value(); @@ -350,7 +352,7 @@ class EnhancedThreadLocal : public NonCopyable { */ template requires std::invocable && - std::convertible_to, T> + std::convertible_to, T> auto getOrCreate(Factory&& factory) -> T& { auto tid = std::this_thread::get_id(); std::unique_lock lock(mutex_); diff --git a/atom/async/utils/daemon.hpp b/atom/async/utils/daemon.hpp index 9e53053d..94d67f87 100644 --- a/atom/async/utils/daemon.hpp +++ b/atom/async/utils/daemon.hpp @@ -56,7 +56,7 @@ Description: Daemon process implementation (Header-Only Library) // External Dependencies (assumed to be available) #include "atom/utils/time/time.hpp" // Time utilities -#include "spdlog/spdlog.h" // Logging library +#include "spdlog/spdlog.h" // Logging library namespace atom::async { @@ -426,8 +426,8 @@ inline auto DaemonGuard::toString() const noexcept -> std::string { } template -auto DaemonGuard::realStart(int argc, char** argv, const Callback& mainCb) - -> int { +auto DaemonGuard::realStart(int argc, char** argv, + const Callback& mainCb) -> int { try { if (argv == nullptr && argc > 0) { throw DaemonException( @@ -459,8 +459,8 @@ auto DaemonGuard::realStart(int argc, char** argv, const Callback& mainCb) } template -auto DaemonGuard::realStartModern(std::span args, const Callback& mainCb) - -> int { +auto DaemonGuard::realStartModern(std::span args, + const Callback& mainCb) -> int { try { if (args.empty() || args[0] == nullptr) { throw DaemonException( @@ -634,9 +634,8 @@ auto DaemonGuard::realDaemon(int argc, char** argv, } template -auto DaemonGuard::realDaemonModern(std::span args, - [[maybe_unused]] const Callback& mainCb) - -> int { +auto DaemonGuard::realDaemonModern( + std::span args, [[maybe_unused]] const Callback& mainCb) -> int { try { if (args.empty() || args[0] == nullptr) { throw DaemonException( @@ -849,8 +848,8 @@ auto DaemonGuard::startDaemon(int argc, char** argv, const Callback& mainCb, template auto DaemonGuard::startDaemonModern(std::span args, - const Callback& mainCb, bool isDaemonParam) - -> int { + const Callback& mainCb, + bool isDaemonParam) -> int { try { if (args.empty() || args[0] == nullptr) { throw DaemonException( diff --git a/atom/async/utils/timer.cpp b/atom/async/utils/timer.cpp index 96b5e482..7037a616 100644 --- a/atom/async/utils/timer.cpp +++ b/atom/async/utils/timer.cpp @@ -26,14 +26,18 @@ TimerTask::TimerTask(std::function func, unsigned int delay, m_delay(delay), m_repeatCount(repeatCount), m_priority(priority) { - std::cout << "[DEBUG] TimerTask constructor: delay = " << delay << ", repeatCount = " << repeatCount << ", priority = " << priority << std::endl; + std::cout << "[DEBUG] TimerTask constructor: delay = " << delay + << ", repeatCount = " << repeatCount + << ", priority = " << priority << std::endl; if (!func) { throw std::invalid_argument("Function cannot be null"); } if (delay == 0) { - throw std::invalid_argument("Delay must be greater than 0 (TimerTask constructor received: " + std::to_string(delay) + ")"); + throw std::invalid_argument( + "Delay must be greater than 0 (TimerTask constructor received: " + + std::to_string(delay) + ")"); } if (repeatCount < -1) { @@ -84,7 +88,8 @@ void Timer::validateTaskParams([[maybe_unused]] unsigned int delay, // The delay>0 validation is enforced in TimerTask constructor if (repeatCount < -1) { - throw std::invalid_argument("RepeatCount must be >= -1 (received: " + std::to_string(repeatCount) + ")"); + throw std::invalid_argument("RepeatCount must be >= -1 (received: " + + std::to_string(repeatCount) + ")"); } } @@ -116,7 +121,9 @@ Timer::Timer() noexcept(false) { std::cout << "[DEBUG] Using ASIO mode" << std::endl; try { m_ioContext = std::make_unique(); - m_work = std::make_unique>(m_ioContext->get_executor()); + m_work = std::make_unique< + asio::executor_work_guard>( + m_ioContext->get_executor()); m_asioTimer = std::make_unique(*m_ioContext); std::thread([this]() { @@ -127,24 +134,29 @@ Timer::Timer() noexcept(false) { } }).detach(); - std::cout << "[DEBUG] ASIO timer initialized successfully" << std::endl; + std::cout << "[DEBUG] ASIO timer initialized successfully" + << std::endl; } catch (const std::exception &e) { - std::cout << "[DEBUG] ASIO timer initialization failed: " << e.what() << std::endl; - throw std::runtime_error(std::string("Failed to create asio timer: ") + - e.what()); + std::cout << "[DEBUG] ASIO timer initialization failed: " + << e.what() << std::endl; + throw std::runtime_error( + std::string("Failed to create asio timer: ") + e.what()); } #else std::cout << "[DEBUG] Using non-ASIO mode" << std::endl; - // Don't start the thread immediately - start it when first task is added - // This prevents race conditions during object construction + // Don't start the thread immediately - start it when first task is + // added This prevents race conditions during object construction #endif - std::cout << "[DEBUG] Timer constructor completed successfully" << std::endl; - } catch (const std::exception& e) { - std::cout << "[DEBUG] Timer constructor failed: " << e.what() << std::endl; + std::cout << "[DEBUG] Timer constructor completed successfully" + << std::endl; + } catch (const std::exception &e) { + std::cout << "[DEBUG] Timer constructor failed: " << e.what() + << std::endl; throw; } catch (...) { - std::cout << "[DEBUG] Timer constructor failed with unknown exception" << std::endl; + std::cout << "[DEBUG] Timer constructor failed with unknown exception" + << std::endl; throw; } } @@ -171,7 +183,9 @@ void Timer::cancelAllTasks() noexcept { m_ioContext->stop(); m_ioContext->restart(); if (!m_stop.load(std::memory_order_acquire)) { - m_work = std::make_unique>(m_ioContext->get_executor()); + m_work = std::make_unique< + asio::executor_work_guard>( + m_ioContext->get_executor()); } } #elif defined(ATOM_USE_BOOST_LOCKFREE) diff --git a/atom/async/utils/timer.hpp b/atom/async/utils/timer.hpp index dee57d84..7f95ed84 100644 --- a/atom/async/utils/timer.hpp +++ b/atom/async/utils/timer.hpp @@ -104,7 +104,9 @@ class TimerTask { * @brief Checks if the task is valid (has a function). * @return true if the task is valid, false otherwise. */ - [[nodiscard]] auto isValid() const noexcept -> bool { return m_func != nullptr; } + [[nodiscard]] auto isValid() const noexcept -> bool { + return m_func != nullptr; + } std::function m_func; ///< The function to be executed. unsigned int m_delay; ///< The delay before the first execution. @@ -147,7 +149,7 @@ class Timer { * @throws std::invalid_argument If the function is null or delay is invalid */ template - // requires Invocable // Temporarily disabled + // requires Invocable // Temporarily disabled [[nodiscard]] auto setTimeout(Function &&func, unsigned int delay, Args &&...args) noexcept(false) -> EnhancedFuture>; @@ -257,7 +259,8 @@ class Timer { #ifdef ATOM_USE_ASIO void asioRun() noexcept; std::unique_ptr m_ioContext; - std::unique_ptr> m_work; + std::unique_ptr> + m_work; std::unique_ptr m_asioTimer; #endif @@ -334,11 +337,12 @@ class Timer { }; template - // requires Invocable // Temporarily disabled +// requires Invocable // Temporarily disabled auto Timer::setTimeout(Function &&func, unsigned int delay, Args &&...args) noexcept(false) -> EnhancedFuture> { - std::cout << "[DEBUG] setTimeout ENTRY: delay = " << delay << ", type = " << typeid(delay).name() << std::endl; + std::cout << "[DEBUG] setTimeout ENTRY: delay = " << delay + << ", type = " << typeid(delay).name() << std::endl; validateTaskParams(delay, 1); // Ensure the timer thread is started before adding tasks @@ -384,8 +388,10 @@ auto Timer::setTimeout(Function &&func, unsigned int delay, #else { std::scoped_lock lock(m_mutex); - std::cout << "[DEBUG] About to emplace TimerTask with delay: " << delay << std::endl; - std::cout << "[DEBUG] Emplace parameters: func=valid, delay=" << delay << ", repeatCount=1, priority=0" << std::endl; + std::cout << "[DEBUG] About to emplace TimerTask with delay: " << delay + << std::endl; + std::cout << "[DEBUG] Emplace parameters: func=valid, delay=" << delay + << ", repeatCount=1, priority=0" << std::endl; m_taskQueue.emplace([task]() { (*task)(); }, delay, 1, 0); std::cout << "[DEBUG] TimerTask emplaced successfully" << std::endl; } diff --git a/atom/components/CMakeLists.txt b/atom/components/CMakeLists.txt index dd42d731..00bf8c5c 100644 --- a/atom/components/CMakeLists.txt +++ b/atom/components/CMakeLists.txt @@ -1,14 +1,13 @@ -# CMakeLists.txt for Atom-Component -# This project adheres to the GPL3 license. +# CMakeLists.txt for Atom-Component This project adheres to the GPL3 license. # -# Project Details: -# Name: Atom-Component -# Description: Central component library for the Atom framework -# Author: Max Qian -# License: GPL3 +# Project Details: Name: Atom-Component Description: Central component library +# for the Atom framework Author: Max Qian License: GPL3 cmake_minimum_required(VERSION 3.21) -project(atom-component VERSION 1.0.0 LANGUAGES C CXX) +project( + atom-component + VERSION 1.0.0 + LANGUAGES C CXX) # Include standardized module configuration include(${CMAKE_SOURCE_DIR}/cmake/ModuleDependencies.cmake) @@ -19,22 +18,18 @@ set(SOURCES core/component.cpp core/component_pool.cpp core/registry.cpp - # Scripting components scripting/advanced_bindings.cpp scripting/script_engine.cpp scripting/script_sandbox.cpp scripting/scripting_api.cpp - # Lifecycle components lifecycle/dispatch.cpp lifecycle/iteration.cpp lifecycle/lifecycle.cpp - # Data components data/serialization.cpp - data/var.cpp -) + data/var.cpp) # Header files set(HEADERS @@ -56,7 +51,6 @@ set(HEADERS var.hpp module_macro.hpp package.hpp - # Actual implementation headers (in subdirectories) core/component.hpp core/component_pool.hpp @@ -64,20 +58,16 @@ set(HEADERS core/types.hpp core/module_macro.hpp core/package.hpp - scripting/advanced_bindings.hpp scripting/script_engine.hpp scripting/script_sandbox.hpp scripting/scripting_api.hpp - lifecycle/dispatch.hpp lifecycle/iteration.hpp lifecycle/lifecycle.hpp - data/serialization.hpp data/var.hpp - data/type_conversion.hpp -) + data/type_conversion.hpp) # Optional scripting engine support option(ATOM_ENABLE_LUA "Enable Lua scripting support" OFF) @@ -89,38 +79,41 @@ set(OPTIONAL_HEADERS) # Lua support if(ATOM_ENABLE_LUA) - list(APPEND OPTIONAL_SOURCES scripting/lua_engine.cpp) - list(APPEND OPTIONAL_HEADERS scripting/lua_engine.hpp) - message(STATUS "Lua scripting support enabled") - - # Find Lua - find_package(Lua QUIET) - if(LUA_FOUND) - list(APPEND LIBS ${LUA_LIBRARIES}) - include_directories(${LUA_INCLUDE_DIR}) - add_definitions(-DATOM_ENABLE_LUA=1) - else() - message(WARNING "Lua not found, disabling Lua support") - set(ATOM_ENABLE_LUA OFF) - endif() + list(APPEND OPTIONAL_SOURCES scripting/lua_engine.cpp) + list(APPEND OPTIONAL_HEADERS scripting/lua_engine.hpp) + message(STATUS "Lua scripting support enabled") + + # Find Lua + find_package(Lua QUIET) + if(LUA_FOUND) + list(APPEND LIBS ${LUA_LIBRARIES}) + include_directories(${LUA_INCLUDE_DIR}) + add_definitions(-DATOM_ENABLE_LUA=1) + else() + message(WARNING "Lua not found, disabling Lua support") + set(ATOM_ENABLE_LUA OFF) + endif() endif() # Python support if(ATOM_ENABLE_PYTHON) - list(APPEND OPTIONAL_SOURCES scripting/python_engine.cpp) - list(APPEND OPTIONAL_HEADERS scripting/python_engine.hpp) - message(STATUS "Python scripting support enabled") - - # Find Python - find_package(Python3 COMPONENTS Interpreter Development QUIET) - if(Python3_FOUND) - list(APPEND LIBS ${Python3_LIBRARIES}) - include_directories(${Python3_INCLUDE_DIRS}) - add_definitions(-DATOM_ENABLE_PYTHON=1) - else() - message(WARNING "Python3 not found, disabling Python support") - set(ATOM_ENABLE_PYTHON OFF) - endif() + list(APPEND OPTIONAL_SOURCES scripting/python_engine.cpp) + list(APPEND OPTIONAL_HEADERS scripting/python_engine.hpp) + message(STATUS "Python scripting support enabled") + + # Find Python + find_package( + Python3 + COMPONENTS Interpreter Development + QUIET) + if(Python3_FOUND) + list(APPEND LIBS ${Python3_LIBRARIES}) + include_directories(${Python3_INCLUDE_DIRS}) + add_definitions(-DATOM_ENABLE_PYTHON=1) + else() + message(WARNING "Python3 not found, disabling Python support") + set(ATOM_ENABLE_PYTHON OFF) + endif() endif() # Dependencies - link to the import libraries @@ -133,18 +126,15 @@ link_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../build/extra) # Find fmt if not already found if(NOT TARGET fmt::fmt) - find_package(fmt QUIET) + find_package(fmt QUIET) endif() -set(LIBS - atom-error - loguru - $<$:fmt::fmt> - ${CMAKE_THREAD_LIBS_INIT} -) +set(LIBS atom-error loguru $<$:fmt::fmt> + ${CMAKE_THREAD_LIBS_INIT}) # Create library target (shared library for components) -add_library(atom-component SHARED ${SOURCES} ${HEADERS} ${OPTIONAL_SOURCES} ${OPTIONAL_HEADERS}) +add_library(atom-component SHARED ${SOURCES} ${HEADERS} ${OPTIONAL_SOURCES} + ${OPTIONAL_HEADERS}) # Configure module using standardized function atom_configure_module(atom-component) @@ -157,96 +147,95 @@ target_compile_features(atom-component PUBLIC cxx_std_20) # Install headers install(FILES ${HEADERS} ${OPTIONAL_HEADERS} - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/atom/components -) + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/atom/components) # Export targets (commented out for now due to dependency export issues) -# install(EXPORT ${PROJECT_NAME}Targets -# FILE ${PROJECT_NAME}Targets.cmake -# NAMESPACE ${PROJECT_NAME}:: -# DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} -# ) - -# Install headers -# Install backwards compatibility headers -install(FILES - advanced_bindings.hpp - component.hpp - component_pool.hpp - dispatch.hpp - iteration.hpp - lifecycle.hpp - lua_engine.hpp - python_engine.hpp - script_engine.hpp - script_sandbox.hpp - scripting_api.hpp - serialization.hpp - type_conversion.hpp - types.hpp - var.hpp - module_macro.hpp - package.hpp - ${OPTIONAL_HEADERS} - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/atom/components -) +# install(EXPORT ${PROJECT_NAME}Targets FILE ${PROJECT_NAME}Targets.cmake +# NAMESPACE ${PROJECT_NAME}:: DESTINATION +# ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} ) + +# Install headers Install backwards compatibility headers +install( + FILES advanced_bindings.hpp + component.hpp + component_pool.hpp + dispatch.hpp + iteration.hpp + lifecycle.hpp + lua_engine.hpp + python_engine.hpp + script_engine.hpp + script_sandbox.hpp + scripting_api.hpp + serialization.hpp + type_conversion.hpp + types.hpp + var.hpp + module_macro.hpp + package.hpp + ${OPTIONAL_HEADERS} + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/atom/components) # Install implementation headers in subdirectories -install(DIRECTORY core/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/atom/components/core - FILES_MATCHING PATTERN "*.hpp" -) -install(DIRECTORY scripting/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/atom/components/scripting - FILES_MATCHING PATTERN "*.hpp" -) -install(DIRECTORY lifecycle/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/atom/components/lifecycle - FILES_MATCHING PATTERN "*.hpp" -) -install(DIRECTORY data/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/atom/components/data - FILES_MATCHING PATTERN "*.hpp" -) +install( + DIRECTORY core/ + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/atom/components/core + FILES_MATCHING + PATTERN "*.hpp") +install( + DIRECTORY scripting/ + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/atom/components/scripting + FILES_MATCHING + PATTERN "*.hpp") +install( + DIRECTORY lifecycle/ + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/atom/components/lifecycle + FILES_MATCHING + PATTERN "*.hpp") +install( + DIRECTORY data/ + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/atom/components/data + FILES_MATCHING + PATTERN "*.hpp") # Testing option(BUILD_TESTS "Build test suite" OFF) if(BUILD_TESTS AND ATOM_BUILD_TESTS) - enable_testing() - add_subdirectory(tests) + enable_testing() + add_subdirectory(tests) endif() # Documentation find_package(Doxygen QUIET) if(DOXYGEN_FOUND) - set(DOXYGEN_IN ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in) - set(DOXYGEN_OUT ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile) - - if(EXISTS ${DOXYGEN_IN}) - configure_file(${DOXYGEN_IN} ${DOXYGEN_OUT} @ONLY) - - add_custom_target(docs - COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_OUT} - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - COMMENT "Generating API documentation with Doxygen" - VERBATIM - ) - endif() + set(DOXYGEN_IN ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in) + set(DOXYGEN_OUT ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile) + + if(EXISTS ${DOXYGEN_IN}) + configure_file(${DOXYGEN_IN} ${DOXYGEN_OUT} @ONLY) + + add_custom_target( + docs + COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_OUT} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Generating API documentation with Doxygen" + VERBATIM) + endif() endif() # Package configuration include(CMakePackageConfigHelpers) configure_package_config_file( - "${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in" - "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" - INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} -) + "${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}) write_basic_package_version_file( - "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" - VERSION ${PROJECT_VERSION} - COMPATIBILITY SameMajorVersion -) - -install(FILES - "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" - "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} -) + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" + VERSION ${PROJECT_VERSION} + COMPATIBILITY SameMajorVersion) + +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}) diff --git a/atom/components/component.template b/atom/components/component.template index 0edefdec..08a41e96 100644 --- a/atom/components/component.template +++ b/atom/components/component.template @@ -95,4 +95,3 @@ if constexpr (Traits::arity == 8) { name, group, description, std::function(std::forward(func))); } - diff --git a/atom/components/core/component.cpp b/atom/components/core/component.cpp index ecf05c81..dd216589 100644 --- a/atom/components/core/component.cpp +++ b/atom/components/core/component.cpp @@ -673,7 +673,7 @@ auto Component::getVariableNames() const -> std::vector { } template -void Component::def(std::string_view name, VarType Class::* var, +void Component::def(std::string_view name, VarType Class::*var, std::string_view group, std::string_view description) { if (!var) { throw std::invalid_argument("Member variable pointer cannot be null"); diff --git a/atom/components/core/component.hpp b/atom/components/core/component.hpp index 4b24c4c7..642d8ef0 100644 --- a/atom/components/core/component.hpp +++ b/atom/components/core/component.hpp @@ -15,9 +15,9 @@ Description: Basic Component Definition #ifndef ATOM_COMPONENT_HPP #define ATOM_COMPONENT_HPP +#include "../data/var.hpp" #include "../lifecycle/dispatch.hpp" #include "module_macro.hpp" -#include "../data/var.hpp" #include "atom/memory/memory_pool.hpp" #include "atom/memory/object.hpp" @@ -562,7 +562,7 @@ class alignas(64) Component : public std::enable_shared_from_this { * @param description Command description, empty by default. */ template - void def(std::string_view name, VarType Class::* var, + void def(std::string_view name, VarType Class::*var, std::string_view group = "", std::string_view description = ""); /** @@ -614,7 +614,7 @@ class alignas(64) Component : public std::enable_shared_from_this { template requires Pointer || SmartPointer || std::is_same_v> - void def(std::string_view name, MemberType Class::* var, + void def(std::string_view name, MemberType Class::*var, const InstanceType& instance, std::string_view group = "", std::string_view description = ""); @@ -632,7 +632,7 @@ class alignas(64) Component : public std::enable_shared_from_this { template requires Pointer || SmartPointer || std::is_same_v> - void def(std::string_view name, const MemberType Class::* var, + void def(std::string_view name, const MemberType Class::*var, const InstanceType& instance, std::string_view group = "", std::string_view description = ""); @@ -786,14 +786,22 @@ class alignas(64) Component : public std::enable_shared_from_this { // 定义条件检查宏 #define CONDITION_EQ std::equality_comparable -#define CONDITION_LT \ - requires(T a, T b) { {a < b}->std::convertible_to; } -#define CONDITION_GT \ - requires(T a, T b) { {a > b}->std::convertible_to; } -#define CONDITION_LE \ - requires(T a, T b) { {a <= b}->std::convertible_to; } -#define CONDITION_GE \ - requires(T a, T b) { {a >= b}->std::convertible_to; } +#define CONDITION_LT \ + requires(T a, T b) { \ + { a < b } -> std::convertible_to; \ + } +#define CONDITION_GT \ + requires(T a, T b) { \ + { a > b } -> std::convertible_to; \ + } +#define CONDITION_LE \ + requires(T a, T b) { \ + { a <= b } -> std::convertible_to; \ + } +#define CONDITION_GE \ + requires(T a, T b) { \ + { a >= b } -> std::convertible_to; \ + } // 注册操作符的通用宏 #define REGISTER_OPERATOR(type_name, name, op, condition, description) \ @@ -887,8 +895,8 @@ class alignas(64) Component : public std::enable_shared_from_this { * @return Command execution result */ template - [[gnu::hot]] auto fastDispatch(std::string_view name, Args&&... args) - -> std::any { + [[gnu::hot]] auto fastDispatch(std::string_view name, + Args&&... args) -> std::any { // Skip timing for maximum performance in hot paths try { auto result = m_CommandDispatcher_->dispatch( diff --git a/atom/components/core/module_macro.hpp b/atom/components/core/module_macro.hpp index 29995f6a..3edfc053 100644 --- a/atom/components/core/module_macro.hpp +++ b/atom/components/core/module_macro.hpp @@ -114,12 +114,11 @@ #module_name, e.what()); \ } \ } \ - extern "C" auto module_name##_getInstance() \ - -> std::shared_ptr { \ + extern "C" auto module_name##_getInstance()->std::shared_ptr { \ LOG_F(INFO, "Getting instance of module: {}", #module_name); \ return Registry::instance().getComponent(#module_name); \ } \ - extern "C" auto module_name##_getVersion() -> const char* { \ + extern "C" auto module_name##_getVersion()->const char* { \ return ATOM_VERSION; \ } #endif @@ -160,7 +159,7 @@ }; \ inline ModuleInitializer module_initializer; \ } \ - auto module_name##_getInstance() -> std::shared_ptr { \ + auto module_name##_getInstance()->std::shared_ptr { \ return Registry::instance().getComponent(#module_name); \ } #endif diff --git a/atom/components/core/registry.cpp b/atom/components/core/registry.cpp index 9acfa828..cc003b13 100644 --- a/atom/components/core/registry.cpp +++ b/atom/components/core/registry.cpp @@ -348,7 +348,8 @@ auto Registry::getOrLoadComponent(const std::string& name) // Create a simple string representation of missing dependencies std::string missingDepsStr; for (const auto& dep : missingDeps) { - if (!missingDepsStr.empty()) missingDepsStr += ", "; + if (!missingDepsStr.empty()) + missingDepsStr += ", "; missingDepsStr += dep; } spdlog::error( @@ -552,7 +553,8 @@ bool Registry::removeComponent(const std::string& name) { // Create a simple string representation of dependents std::string dependentsStr; for (const auto& dep : dependents) { - if (!dependentsStr.empty()) dependentsStr += ", "; + if (!dependentsStr.empty()) + dependentsStr += ", "; dependentsStr += dep; } spdlog::error( @@ -812,7 +814,8 @@ void Registry::determineInitializationOrder() { // Create a simple string representation of initialization order std::string orderStr; for (const auto& name : initializationOrder_) { - if (!orderStr.empty()) orderStr += ", "; + if (!orderStr.empty()) + orderStr += ", "; orderStr += name; } spdlog::info("Determined initialization order: {}", orderStr); diff --git a/atom/components/core/registry.hpp b/atom/components/core/registry.hpp index 8cf1d2a5..4910a29a 100644 --- a/atom/components/core/registry.hpp +++ b/atom/components/core/registry.hpp @@ -25,9 +25,9 @@ Description: Component Registry for Managing Component Lifecycle #include #include +#include "../lifecycle/lifecycle.hpp" #include "component.hpp" #include "component_pool.hpp" -#include "../lifecycle/lifecycle.hpp" class Component; diff --git a/atom/components/data/serialization.hpp b/atom/components/data/serialization.hpp index 08d65b29..0bd66802 100644 --- a/atom/components/data/serialization.hpp +++ b/atom/components/data/serialization.hpp @@ -29,8 +29,8 @@ and schema validation. #include #include -#include "atom/type/json.hpp" #include "../core/component.hpp" +#include "atom/type/json.hpp" namespace atom::components { diff --git a/atom/components/data/var.cpp b/atom/components/data/var.cpp index 1339ebba..57a3c865 100644 --- a/atom/components/data/var.cpp +++ b/atom/components/data/var.cpp @@ -89,7 +89,8 @@ void VariableManager::removeVariable(const std::string& name) { // This might be an alias entry, find the primary std::string primaryName; for (const auto& [key, value] : variables_) { - if (key != name && !value.alias.empty() && value.alias == name) { + if (key != name && !value.alias.empty() && + value.alias == name) { primaryName = key; break; } diff --git a/atom/components/lifecycle/dispatch.cpp b/atom/components/lifecycle/dispatch.cpp index e4fbde77..3e2154ea 100644 --- a/atom/components/lifecycle/dispatch.cpp +++ b/atom/components/lifecycle/dispatch.cpp @@ -72,10 +72,9 @@ void CommandDispatcher::checkPostcondition(const Command& cmd, } } -auto CommandDispatcher::executeCommand(const Command& cmd, - const std::string& name, - const std::vector& args) - -> std::any { +auto CommandDispatcher::executeCommand( + const Command& cmd, const std::string& name, + const std::vector& args) -> std::any { // spdlog::trace("Entering function: {}", __func__); // Replaced // LOG_SCOPE_FUNCTION @@ -108,8 +107,8 @@ auto CommandDispatcher::executeCommand(const Command& cmd, auto CommandDispatcher::executeWithTimeout( const Command& cmd, const std::string& name, - const std::vector& args, const std::chrono::milliseconds& timeout) - -> std::any { + const std::vector& args, + const std::chrono::milliseconds& timeout) -> std::any { // spdlog::trace("Entering function: {}", __func__); // Replaced // LOG_SCOPE_FUNCTION @@ -163,10 +162,9 @@ auto CommandDispatcher::executeWithTimeout( } } -auto CommandDispatcher::executeWithoutTimeout(const Command& cmd, - const std::string& name, - const std::vector& args) - -> std::any { +auto CommandDispatcher::executeWithoutTimeout( + const Command& cmd, const std::string& name, + const std::vector& args) -> std::any { // spdlog::trace("Entering function: {}", __func__); // Replaced // LOG_SCOPE_FUNCTION Check for nested arguments if (!args.empty() && args.size() == 1 && @@ -180,13 +178,13 @@ auto CommandDispatcher::executeWithoutTimeout(const Command& cmd, return executeFunctions(cmd, args); } -auto CommandDispatcher::executeFunctions(const Command& cmd, - const std::vector& args) - -> std::any { +auto CommandDispatcher::executeFunctions( + const Command& cmd, const std::vector& args) -> std::any { // We already selected the correct overload earlier by signature. // Directly invoke the stored proxy function and surface any type errors. try { - spdlog::info("Executing function for command (skipping hash validation)"); + spdlog::info( + "Executing function for command (skipping hash validation)"); return std::invoke(cmd.func, const_cast&>(args)); } catch (const std::bad_any_cast& e) { spdlog::error("Failed to call function: {}", e.what()); @@ -626,10 +624,10 @@ CommandDispatcher::getCommandArgAndReturnType(std::string_view name) const { return {}; } -auto CommandDispatcher::dispatchHelper(const std::string& name, - const std::vector& args) - -> std::any { +auto CommandDispatcher::dispatchHelper( + const std::string& name, const std::vector& args) -> std::any { // Delegate to the template-based dispatcher which uses signature matching - // and default argument completion for consistent behavior across call sites. + // and default argument completion for consistent behavior across call + // sites. return dispatchHelper>(name, args); } diff --git a/atom/components/lifecycle/dispatch.hpp b/atom/components/lifecycle/dispatch.hpp index b1728473..4fac79ad 100644 --- a/atom/components/lifecycle/dispatch.hpp +++ b/atom/components/lifecycle/dispatch.hpp @@ -277,8 +277,8 @@ class CommandDispatcher { * @return The result of the command execution. */ template - auto dispatchHelper(const std::string& name, const ArgsType& args) - -> std::any; + auto dispatchHelper(const std::string& name, + const ArgsType& args) -> std::any; auto dispatchHelper(const std::string& name, const std::vector& args) -> std::any; @@ -309,8 +309,8 @@ class CommandDispatcher { * @return A vector of completed arguments. */ template - auto completeArgs(const Command& cmd, const ArgsType& args) - -> std::vector; + auto completeArgs(const Command& cmd, + const ArgsType& args) -> std::vector; /** * @brief Checks the precondition of a command. @@ -356,10 +356,9 @@ class CommandDispatcher { * @param args The arguments for the command. * @return The result of the command execution. */ - static auto executeWithoutTimeout(const Command& cmd, - const std::string& name, - const std::vector& args) - -> std::any; + static auto executeWithoutTimeout( + const Command& cmd, const std::string& name, + const std::vector& args) -> std::any; /** * @brief Executes the functions of a command. @@ -507,8 +506,9 @@ template std::move(precondition), std::move(postcondition)}; - // Build a runtime-matching signature based only on argument types (no return type) - // and using raw type_info names to match dispatch-time construction. + // Build a runtime-matching signature based only on argument types (no + // return type) and using raw type_info names to match dispatch-time + // construction. std::string signature = "("; // Use fold expression to append each Args typeid name ((signature += std::string(typeid(Args).name()) + ","), ...); @@ -516,7 +516,8 @@ template signature.pop_back(); } signature += ")"; - spdlog::info("Computed registration signature for '{}' as '{}'", nameStr, signature); + spdlog::info("Computed registration signature for '{}' as '{}'", nameStr, + signature); // Thread-safe operation { @@ -531,7 +532,8 @@ template } } - spdlog::info("Registering command '{}' with signature '{}'", nameStr, signature); + spdlog::info("Registering command '{}' with signature '{}'", nameStr, + signature); commands_[nameStr][signature] = std::move(cmd); groupMap_[nameStr] = groupStr; } @@ -576,7 +578,8 @@ auto CommandDispatcher::dispatchHelper(const std::string& name, } signature += ")"; - spdlog::info("Dispatch lookup for '{}' with signature '{}'", name, signature); + spdlog::info("Dispatch lookup for '{}' with signature '{}'", name, + signature); // Lock for thread safety during command lookup std::shared_lock lock(mutex_); @@ -598,11 +601,13 @@ auto CommandDispatcher::dispatchHelper(const std::string& name, // Validate arguments if this is a vector of anys if constexpr (std::is_same_v>) { - // Basic validation of argument count - only when we have explicit arg metadata. - // If argTypes is empty (common when registering without Arg info), skip this check. + // Basic validation of argument count - only when we have explicit arg + // metadata. If argTypes is empty (common when registering without Arg + // info), skip this check. if (!cmd.argTypes.empty() && args.size() > cmd.argTypes.size()) { THROW_INVALID_ARGUMENT( - "Too many arguments for command {}: expected at most {}, got {}", + "Too many arguments for command {}: expected at most {}, got " + "{}", name, cmd.argTypes.size(), args.size()); } } diff --git a/atom/components/lifecycle/iteration.hpp b/atom/components/lifecycle/iteration.hpp index b4e8ed16..345d15d4 100644 --- a/atom/components/lifecycle/iteration.hpp +++ b/atom/components/lifecycle/iteration.hpp @@ -69,10 +69,10 @@ inline constexpr bool is_simd_compatible_v = is_simd_compatible::value; template struct is_batch_processable { template - static auto test(int) - -> decltype(std::declval().batchUpdate(), - std::declval().getUpdateData(), - std::declval().getUpdateDataSize(), std::true_type{}); + static auto test(int) -> decltype(std::declval().batchUpdate(), + std::declval().getUpdateData(), + std::declval().getUpdateDataSize(), + std::true_type{}); template static std::false_type test(...); @@ -497,7 +497,8 @@ struct hash> { std::size_t operator()(const std::vector& vec) const { std::size_t seed = vec.size(); for (const auto& i : vec) { - seed ^= std::hash{}(i) + 0x9e3779b9 + (seed << 6) + (seed >> 2); + seed ^= std::hash{}(i) + 0x9e3779b9 + (seed << 6) + + (seed >> 2); } return seed; } diff --git a/atom/components/scripting/advanced_bindings.hpp b/atom/components/scripting/advanced_bindings.hpp index 6c557b5f..a8da216d 100644 --- a/atom/components/scripting/advanced_bindings.hpp +++ b/atom/components/scripting/advanced_bindings.hpp @@ -25,8 +25,8 @@ for both Lua and Python scripting engines. #include #include -#include "scripting_api.hpp" #include "../data/type_conversion.hpp" +#include "scripting_api.hpp" namespace atom::components::scripting { diff --git a/atom/components/scripting/scripting_api.cpp b/atom/components/scripting/scripting_api.cpp index 99781cb5..a91a5efc 100644 --- a/atom/components/scripting/scripting_api.cpp +++ b/atom/components/scripting/scripting_api.cpp @@ -5,9 +5,9 @@ */ #include "scripting_api.hpp" +#include "../core/registry.hpp" #include "lua_engine.hpp" #include "python_engine.hpp" -#include "../core/registry.hpp" #include #include diff --git a/atom/connection/CMakeLists.txt b/atom/connection/CMakeLists.txt index 4af3eb6b..977a6bd4 100644 --- a/atom/connection/CMakeLists.txt +++ b/atom/connection/CMakeLists.txt @@ -1,13 +1,14 @@ -# CMakeLists.txt for Atom-Connection -# This project is licensed under the terms of the GPL3 license. +# CMakeLists.txt for Atom-Connection This project is licensed under the terms of +# the GPL3 license. # -# Project Name: Atom-Connection -# Description: Connection Between Lithium Drivers, TCP and IPC -# Author: Max Qian -# License: GPL3 +# Project Name: Atom-Connection Description: Connection Between Lithium Drivers, +# TCP and IPC Author: Max Qian License: GPL3 cmake_minimum_required(VERSION 3.21) -project(atom-connection VERSION 1.0.0 LANGUAGES C CXX) +project( + atom-connection + VERSION 1.0.0 + LANGUAGES C CXX) # Include standardized module configuration include(${CMAKE_SOURCE_DIR}/cmake/ModuleDependencies.cmake) @@ -20,23 +21,23 @@ set(SOURCES tcpclient.cpp ttybase.cpp udpclient.cpp - udpserver.cpp -) + udpserver.cpp) # Check for ASIO support and add async sources if available find_package(asio QUIET) if(Asio_FOUND) - list(APPEND SOURCES - async_fifoclient.cpp - async_fifoserver.cpp - async_sockethub.cpp - async_tcpclient.cpp - async_udpclient.cpp - async_udpserver.cpp - ) - message(STATUS "ASIO found - async connection features enabled") + list( + APPEND + SOURCES + async_fifoclient.cpp + async_fifoserver.cpp + async_sockethub.cpp + async_tcpclient.cpp + async_udpclient.cpp + async_udpserver.cpp) + message(STATUS "ASIO found - async connection features enabled") else() - message(STATUS "ASIO not found - async connection features disabled") + message(STATUS "ASIO not found - async connection features disabled") endif() # Headers @@ -47,45 +48,35 @@ set(HEADERS tcpclient.hpp ttybase.hpp udpclient.hpp - udpserver.hpp -) + udpserver.hpp) # Add async headers if ASIO is available if(asio_FOUND) - list(APPEND HEADERS - async_fifoclient.hpp - async_fifoserver.hpp - async_sockethub.hpp - async_tcpclient.hpp - async_udpclient.hpp - async_udpserver.hpp - ) + list( + APPEND + HEADERS + async_fifoclient.hpp + async_fifoserver.hpp + async_sockethub.hpp + async_tcpclient.hpp + async_udpclient.hpp + async_udpserver.hpp) endif() -if (ATOM_USE_SSH) - list(APPEND SOURCES - sshclient.cpp - sshserver.cpp - ) - list(APPEND HEADERS - sshclient.hpp - sshserver.hpp - ) +if(ATOM_USE_SSH) + list(APPEND SOURCES sshclient.cpp sshserver.cpp) + list(APPEND HEADERS sshclient.hpp sshserver.hpp) endif() # Dependencies -set(LIBS - loguru - ${CMAKE_THREAD_LIBS_INIT} - ${OPENSSL_LIBRARIES} -) +set(LIBS loguru ${CMAKE_THREAD_LIBS_INIT} ${OPENSSL_LIBRARIES}) -if (WIN32) - list(APPEND LIBS ws2_32 mswsock) +if(WIN32) + list(APPEND LIBS ws2_32 mswsock) endif() -if (ATOM_USE_SSH) - list(APPEND LIBS ${LIBSSH_LIBRARIES}) +if(ATOM_USE_SSH) + list(APPEND LIBS ${LIBSSH_LIBRARIES}) endif() # Create library target @@ -99,5 +90,4 @@ target_link_libraries(atom-connection PRIVATE ${LIBS}) # Install headers install(FILES ${HEADERS} - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/atom/connection -) + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/atom/connection) diff --git a/atom/connection/async_sockethub.cpp b/atom/connection/async_sockethub.cpp index 72dba1f4..cf9da5f1 100644 --- a/atom/connection/async_sockethub.cpp +++ b/atom/connection/async_sockethub.cpp @@ -550,7 +550,7 @@ class SocketHub::Impl { rate_limiter_.releaseConnection(client->getRemoteAddress()); log(LogLevel::INFO_LEVEL, "Client " + std::to_string(client_id) + - " disconnected. Reason: " + reason); + " disconnected. Reason: " + reason); } } @@ -569,8 +569,8 @@ class SocketHub::Impl { if (!client_exists) { log(LogLevel::WARNING_LEVEL, "Cannot add non-existent client " + - std::to_string(client_id) + - " to group " + group_name); + std::to_string(client_id) + + " to group " + group_name); return; } @@ -580,12 +580,13 @@ class SocketHub::Impl { // Create the group if it doesn't exist groups_[group_name] = std::unordered_set{client_id}; log(LogLevel::INFO_LEVEL, "Created group " + group_name + - " and added client " + - std::to_string(client_id)); + " and added client " + + std::to_string(client_id)); } else { it->second.insert(client_id); - log(LogLevel::INFO_LEVEL, "Added client " + std::to_string(client_id) + - " to group " + group_name); + log(LogLevel::INFO_LEVEL, "Added client " + + std::to_string(client_id) + + " to group " + group_name); } } @@ -595,8 +596,9 @@ class SocketHub::Impl { auto it = groups_.find(group_name); if (it != groups_.end()) { it->second.erase(client_id); - log(LogLevel::INFO_LEVEL, "Removed client " + std::to_string(client_id) + - " from group " + group_name); + log(LogLevel::INFO_LEVEL, "Removed client " + + std::to_string(client_id) + + " from group " + group_name); } } @@ -615,9 +617,9 @@ class SocketHub::Impl { sendMessageToClient(client_id, message); } - log(LogLevel::DEBUG_LEVEL, "Broadcasted message to group " + group_name + - " (" + std::to_string(client_ids.size()) + - " clients)"); + log(LogLevel::DEBUG_LEVEL, + "Broadcasted message to group " + group_name + " (" + + std::to_string(client_ids.size()) + " clients)"); } void setAuthenticator( @@ -630,7 +632,7 @@ class SocketHub::Impl { void requireAuthentication(bool require) { require_authentication_ = require; log(LogLevel::INFO_LEVEL, "Authentication requirement set to: " + - std::string(require ? "true" : "false")); + std::string(require ? "true" : "false")); } void setClientMetadata(size_t client_id, const std::string& key, @@ -646,8 +648,9 @@ class SocketHub::Impl { if (client) { client->setMetadata(key, value); - log(LogLevel::DEBUG_LEVEL, "Set metadata '" + key + "' for client " + - std::to_string(client_id)); + log(LogLevel::DEBUG_LEVEL, "Set metadata '" + key + + "' for client " + + std::to_string(client_id)); } } @@ -1133,9 +1136,9 @@ class SocketHub::Impl { } if (!timeout_clients.empty()) { - log(LogLevel::INFO_LEVEL, "Disconnected " + - std::to_string(timeout_clients.size()) + - " clients due to timeout"); + log(LogLevel::INFO_LEVEL, + "Disconnected " + std::to_string(timeout_clients.size()) + + " clients due to timeout"); } } diff --git a/atom/connection/async_sockethub.hpp b/atom/connection/async_sockethub.hpp index cc2acd0d..fa3d9cdf 100644 --- a/atom/connection/async_sockethub.hpp +++ b/atom/connection/async_sockethub.hpp @@ -17,7 +17,13 @@ namespace atom::async::connection { class Client; struct Message; -enum class LogLevel { DEBUG_LEVEL, INFO_LEVEL, WARNING_LEVEL, ERROR_LEVEL, FATAL_LEVEL }; +enum class LogLevel { + DEBUG_LEVEL, + INFO_LEVEL, + WARNING_LEVEL, + ERROR_LEVEL, + FATAL_LEVEL +}; // Configuration structure for the SocketHub struct SocketHubConfig { diff --git a/atom/connection/fifoclient.cpp b/atom/connection/fifoclient.cpp index 8a2188d8..af694faa 100644 --- a/atom/connection/fifoclient.cpp +++ b/atom/connection/fifoclient.cpp @@ -875,8 +875,9 @@ FifoClient::readAsyncWithFuture( return m_impl->readAsyncWithFuture(maxSize, timeout); } -auto FifoClient::open(std::optional timeout [[maybe_unused]]) - -> type::expected { +auto FifoClient::open( + std::optional timeout + [[maybe_unused]]) -> type::expected { if (!m_impl) { return type::unexpected(make_error_code(FifoError::NotOpen)); } diff --git a/atom/connection/fifoserver.hpp b/atom/connection/fifoserver.hpp index 3dd58df5..59ef7a99 100644 --- a/atom/connection/fifoserver.hpp +++ b/atom/connection/fifoserver.hpp @@ -19,12 +19,12 @@ Description: FIFO Server #include #include -#include "fifoclient.hpp" // For MessagePriority enum #include #include #include #include #include +#include "fifoclient.hpp" // For MessagePriority enum namespace atom::connection { diff --git a/atom/connection/tcpclient.hpp b/atom/connection/tcpclient.hpp index df61719d..a50fdcd1 100644 --- a/atom/connection/tcpclient.hpp +++ b/atom/connection/tcpclient.hpp @@ -123,7 +123,8 @@ struct Task::promise_type { template concept CallbackInvocable = std::invocable || std::invocable&> || - std::invocable || std::invocable; + std::invocable || + std::invocable; /** * @class TcpClient diff --git a/atom/connection/udpclient.cpp b/atom/connection/udpclient.cpp index 7e4d50d1..8a72e272 100644 --- a/atom/connection/udpclient.cpp +++ b/atom/connection/udpclient.cpp @@ -208,7 +208,7 @@ class UdpClient::Impl { return type::unexpected(UdpError::InvalidParameter); } - struct sockaddr_in address{}; + struct sockaddr_in address {}; address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons(port); @@ -368,7 +368,7 @@ class UdpClient::Impl { return type::unexpected(UdpError::InvalidParameter); } - struct addrinfo hints{}; + struct addrinfo hints {}; struct addrinfo* result = nullptr; hints.ai_family = AF_INET; @@ -425,7 +425,7 @@ class UdpClient::Impl { return type::unexpected(UdpError::BroadcastError); } - struct sockaddr_in broadcastAddr{}; + struct sockaddr_in broadcastAddr {}; broadcastAddr.sin_family = AF_INET; broadcastAddr.sin_port = htons(port); @@ -503,7 +503,7 @@ class UdpClient::Impl { } #else // Use epoll for timeout on Linux/Unix - struct epoll_event event{}; + struct epoll_event event {}; event.events = EPOLLIN; event.data.fd = socket_; @@ -528,7 +528,7 @@ class UdpClient::Impl { } std::vector data(maxSize); - struct sockaddr_in clientAddress{}; + struct sockaddr_in clientAddress {}; socklen_t clientAddressLength = sizeof(clientAddress); ssize_t bytesRead = @@ -577,7 +577,7 @@ class UdpClient::Impl { return type::unexpected(UdpError::InvalidParameter); } - struct ip_mreq mreq{}; + struct ip_mreq mreq {}; // Set the multicast IP address if (inet_pton(AF_INET, groupAddress.c_str(), &mreq.imr_multiaddr) <= @@ -618,7 +618,7 @@ class UdpClient::Impl { return type::unexpected(UdpError::InvalidParameter); } - struct ip_mreq mreq{}; + struct ip_mreq mreq {}; // Set the multicast IP address if (inet_pton(AF_INET, groupAddress.c_str(), &mreq.imr_multiaddr) <= @@ -668,7 +668,7 @@ class UdpClient::Impl { return type::unexpected(UdpError::MulticastError); } - struct sockaddr_in multicastAddr{}; + struct sockaddr_in multicastAddr {}; multicastAddr.sin_family = AF_INET; multicastAddr.sin_port = htons(port); @@ -822,7 +822,7 @@ class UdpClient::Impl { std::vector buffer(bufferSize); while (!receivingStopped_ && !stopToken.stop_requested()) { - struct sockaddr_in clientAddress{}; + struct sockaddr_in clientAddress {}; socklen_t clientAddressLength = sizeof(clientAddress); ssize_t bytesRead = diff --git a/atom/containers/CMakeLists.txt b/atom/containers/CMakeLists.txt index acbc07ae..8ec7201a 100644 --- a/atom/containers/CMakeLists.txt +++ b/atom/containers/CMakeLists.txt @@ -1,20 +1,19 @@ cmake_minimum_required(VERSION 3.20) -project(atom-containers VERSION 1.0.0 LANGUAGES C CXX) +project( + atom-containers + VERSION 1.0.0 + LANGUAGES C CXX) # Include standardized module configuration include(${CMAKE_SOURCE_DIR}/cmake/ModuleDependencies.cmake) # Headers -set(HEADERS - boost_containers.hpp - graph.hpp - high_performance.hpp - intrusive.hpp - lockfree.hpp -) +set(HEADERS boost_containers.hpp graph.hpp high_performance.hpp intrusive.hpp + lockfree.hpp) # Configure module using standardized function atom_configure_module(atom-containers HEADER_ONLY) # Install headers -install(FILES ${HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/atom/containers) +install(FILES ${HEADERS} + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/atom/containers) diff --git a/atom/containers/xmake.lua b/atom/containers/xmake.lua index b8e1c771..c6cc7fac 100644 --- a/atom/containers/xmake.lua +++ b/atom/containers/xmake.lua @@ -48,4 +48,3 @@ target("atom-containers") os.cp(hdr, headerdir) end end) - diff --git a/atom/error/CMakeLists.txt b/atom/error/CMakeLists.txt index b55ebab5..55afa8d4 100644 --- a/atom/error/CMakeLists.txt +++ b/atom/error/CMakeLists.txt @@ -1,13 +1,14 @@ -# CMakeLists.txt for Atom-Error -# This project is licensed under the terms of the GPL3 license. +# CMakeLists.txt for Atom-Error This project is licensed under the terms of the +# GPL3 license. # -# Project Name: Atom-Error -# Description: Atom Error Library -# Author: Max Qian +# Project Name: Atom-Error Description: Atom Error Library Author: Max Qian # License: GPL3 cmake_minimum_required(VERSION 3.21) -project(atom-error VERSION 1.0.0 LANGUAGES C CXX) +project( + atom-error + VERSION 1.0.0 + LANGUAGES C CXX) # Include standardized module configuration include(${CMAKE_SOURCE_DIR}/cmake/ModuleDependencies.cmake) @@ -20,8 +21,7 @@ set(SOURCES error_context.cpp error_handler.cpp error_recovery.cpp - error_formatter.cpp -) + error_formatter.cpp) # Headers set(HEADERS @@ -31,69 +31,73 @@ set(HEADERS error_context.hpp error_handler.hpp error_recovery.hpp - error_formatter.hpp -) + error_formatter.hpp) # Optional stacktrace library support -option(ATOM_USE_CPPTRACE "Enable cpptrace support for enhanced stack traces" OFF) -option(ATOM_USE_BACKWARD_CPP "Enable backward-cpp support for enhanced stack traces" OFF) -option(ATOM_USE_BOOST_STACKTRACE "Enable boost::stacktrace support for enhanced stack traces" OFF) +option(ATOM_USE_CPPTRACE "Enable cpptrace support for enhanced stack traces" + OFF) +option(ATOM_USE_BACKWARD_CPP + "Enable backward-cpp support for enhanced stack traces" OFF) +option(ATOM_USE_BOOST_STACKTRACE + "Enable boost::stacktrace support for enhanced stack traces" OFF) # Dependencies -set(LIBS -) +set(LIBS) # Platform-specific libraries -if (WIN32) - list(APPEND LIBS dbghelp) +if(WIN32) + list(APPEND LIBS dbghelp) endif() -if (LINUX) - list(APPEND LIBS dl) +if(LINUX) + list(APPEND LIBS dl) endif() # Optional external stacktrace libraries set(STACKTRACE_DEFINITIONS "") -if (ATOM_USE_CPPTRACE) - find_package(cpptrace QUIET) - if (cpptrace_FOUND) - list(APPEND LIBS cpptrace::cpptrace) - list(APPEND STACKTRACE_DEFINITIONS ATOM_USE_CPPTRACE) - message(STATUS "cpptrace support enabled") - else() - message(WARNING "cpptrace requested but not found") - endif() +if(ATOM_USE_CPPTRACE) + find_package(cpptrace QUIET) + if(cpptrace_FOUND) + list(APPEND LIBS cpptrace::cpptrace) + list(APPEND STACKTRACE_DEFINITIONS ATOM_USE_CPPTRACE) + message(STATUS "cpptrace support enabled") + else() + message(WARNING "cpptrace requested but not found") + endif() endif() -if (ATOM_USE_BACKWARD_CPP) - find_package(Backward QUIET) - if (Backward_FOUND) - list(APPEND LIBS Backward::Backward) - list(APPEND STACKTRACE_DEFINITIONS ATOM_USE_BACKWARD_CPP) - message(STATUS "backward-cpp support enabled") - else() - message(WARNING "backward-cpp requested but not found") - endif() +if(ATOM_USE_BACKWARD_CPP) + find_package(Backward QUIET) + if(Backward_FOUND) + list(APPEND LIBS Backward::Backward) + list(APPEND STACKTRACE_DEFINITIONS ATOM_USE_BACKWARD_CPP) + message(STATUS "backward-cpp support enabled") + else() + message(WARNING "backward-cpp requested but not found") + endif() endif() -if (ATOM_USE_BOOST_STACKTRACE) - find_package(Boost COMPONENTS stacktrace_basic stacktrace_backtrace stacktrace_addr2line QUIET) - if (Boost_FOUND) - list(APPEND LIBS Boost::stacktrace_basic) - list(APPEND STACKTRACE_DEFINITIONS ATOM_USE_BOOST_STACKTRACE) - message(STATUS "boost::stacktrace support enabled") - else() - message(WARNING "boost::stacktrace requested but not found") - endif() +if(ATOM_USE_BOOST_STACKTRACE) + find_package( + Boost + COMPONENTS stacktrace_basic stacktrace_backtrace stacktrace_addr2line + QUIET) + if(Boost_FOUND) + list(APPEND LIBS Boost::stacktrace_basic) + list(APPEND STACKTRACE_DEFINITIONS ATOM_USE_BOOST_STACKTRACE) + message(STATUS "boost::stacktrace support enabled") + else() + message(WARNING "boost::stacktrace requested but not found") + endif() endif() # Create library target (shared library for error module) add_library(atom-error SHARED ${SOURCES} ${HEADERS}) # Apply stacktrace library definitions -if (STACKTRACE_DEFINITIONS) - target_compile_definitions(atom-error PRIVATE ${STACKTRACE_DEFINITIONS}) +if(STACKTRACE_DEFINITIONS) + target_compile_definitions(atom-error PRIVATE ${STACKTRACE_DEFINITIONS}) endif() # Configure module using standardized function @@ -103,6 +107,4 @@ atom_configure_module(atom-error) target_link_libraries(atom-error PRIVATE ${LIBS}) # Install headers -install(FILES ${HEADERS} - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/atom/error -) +install(FILES ${HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/atom/error) diff --git a/atom/error/error_code.cpp b/atom/error/error_code.cpp index d30c62c7..326199db 100644 --- a/atom/error/error_code.cpp +++ b/atom/error/error_code.cpp @@ -24,116 +24,143 @@ void ErrorCodeMapper::initializeErrorMetadata() { std::call_once(initialized, []() { // File errors errorMetadataMap_[100] = ErrorMetadata( - ErrorSeverity::Error, ErrorCategory::IO, ErrorRecoveryStrategy::Retry, - "File not found", "Check if the file path is correct and the file exists"); - - errorMetadataMap_[101] = ErrorMetadata( - ErrorSeverity::Error, ErrorCategory::IO, ErrorRecoveryStrategy::Retry, - "Cannot open file", "Check file permissions and availability"); - + ErrorSeverity::Error, ErrorCategory::IO, + ErrorRecoveryStrategy::Retry, "File not found", + "Check if the file path is correct and the file exists"); + + errorMetadataMap_[101] = + ErrorMetadata(ErrorSeverity::Error, ErrorCategory::IO, + ErrorRecoveryStrategy::Retry, "Cannot open file", + "Check file permissions and availability"); + errorMetadataMap_[102] = ErrorMetadata( - ErrorSeverity::Error, ErrorCategory::Security, ErrorRecoveryStrategy::UserIntervention, - "Access denied", "Check file permissions or run with appropriate privileges"); - - errorMetadataMap_[103] = ErrorMetadata( - ErrorSeverity::Error, ErrorCategory::IO, ErrorRecoveryStrategy::Retry, - "File read error", "Check file integrity and disk health"); - - errorMetadataMap_[104] = ErrorMetadata( - ErrorSeverity::Error, ErrorCategory::IO, ErrorRecoveryStrategy::Retry, - "File write error", "Check disk space and file permissions"); - - errorMetadataMap_[111] = ErrorMetadata( - ErrorSeverity::Critical, ErrorCategory::System, ErrorRecoveryStrategy::UserIntervention, - "Disk full", "Free up disk space or use a different location"); - + ErrorSeverity::Error, ErrorCategory::Security, + ErrorRecoveryStrategy::UserIntervention, "Access denied", + "Check file permissions or run with appropriate privileges"); + + errorMetadataMap_[103] = + ErrorMetadata(ErrorSeverity::Error, ErrorCategory::IO, + ErrorRecoveryStrategy::Retry, "File read error", + "Check file integrity and disk health"); + + errorMetadataMap_[104] = + ErrorMetadata(ErrorSeverity::Error, ErrorCategory::IO, + ErrorRecoveryStrategy::Retry, "File write error", + "Check disk space and file permissions"); + + errorMetadataMap_[111] = + ErrorMetadata(ErrorSeverity::Critical, ErrorCategory::System, + ErrorRecoveryStrategy::UserIntervention, "Disk full", + "Free up disk space or use a different location"); + // Device errors - errorMetadataMap_[201] = ErrorMetadata( - ErrorSeverity::Error, ErrorCategory::System, ErrorRecoveryStrategy::Retry, - "Device not found", "Check device connection and drivers"); - - errorMetadataMap_[202] = ErrorMetadata( - ErrorSeverity::Error, ErrorCategory::System, ErrorRecoveryStrategy::None, - "Device not supported", "Use a compatible device or update drivers"); - - errorMetadataMap_[203] = ErrorMetadata( - ErrorSeverity::Warning, ErrorCategory::System, ErrorRecoveryStrategy::Retry, - "Device not connected", "Connect the device and try again"); - + errorMetadataMap_[201] = + ErrorMetadata(ErrorSeverity::Error, ErrorCategory::System, + ErrorRecoveryStrategy::Retry, "Device not found", + "Check device connection and drivers"); + + errorMetadataMap_[202] = + ErrorMetadata(ErrorSeverity::Error, ErrorCategory::System, + ErrorRecoveryStrategy::None, "Device not supported", + "Use a compatible device or update drivers"); + + errorMetadataMap_[203] = + ErrorMetadata(ErrorSeverity::Warning, ErrorCategory::System, + ErrorRecoveryStrategy::Retry, "Device not connected", + "Connect the device and try again"); + // Network errors errorMetadataMap_[400] = ErrorMetadata( - ErrorSeverity::Error, ErrorCategory::Network, ErrorRecoveryStrategy::Retry, - "Network connection lost", "Check network connectivity and retry"); - - errorMetadataMap_[401] = ErrorMetadata( - ErrorSeverity::Error, ErrorCategory::Network, ErrorRecoveryStrategy::Retry, - "Connection refused", "Check if the service is running and accessible"); - - errorMetadataMap_[411] = ErrorMetadata( - ErrorSeverity::Warning, ErrorCategory::Network, ErrorRecoveryStrategy::Retry, - "Network timeout", "Check network speed and retry with longer timeout"); - + ErrorSeverity::Error, ErrorCategory::Network, + ErrorRecoveryStrategy::Retry, "Network connection lost", + "Check network connectivity and retry"); + + errorMetadataMap_[401] = + ErrorMetadata(ErrorSeverity::Error, ErrorCategory::Network, + ErrorRecoveryStrategy::Retry, "Connection refused", + "Check if the service is running and accessible"); + + errorMetadataMap_[411] = + ErrorMetadata(ErrorSeverity::Warning, ErrorCategory::Network, + ErrorRecoveryStrategy::Retry, "Network timeout", + "Check network speed and retry with longer timeout"); + // Database errors errorMetadataMap_[500] = ErrorMetadata( - ErrorSeverity::Critical, ErrorCategory::External, ErrorRecoveryStrategy::Retry, - "Database connection failed", "Check database server status and connection parameters"); - - errorMetadataMap_[508] = ErrorMetadata( - ErrorSeverity::Error, ErrorCategory::External, ErrorRecoveryStrategy::Retry, - "Database deadlock", "Transaction will be retried automatically"); - + ErrorSeverity::Critical, ErrorCategory::External, + ErrorRecoveryStrategy::Retry, "Database connection failed", + "Check database server status and connection parameters"); + + errorMetadataMap_[508] = + ErrorMetadata(ErrorSeverity::Error, ErrorCategory::External, + ErrorRecoveryStrategy::Retry, "Database deadlock", + "Transaction will be retried automatically"); + // Memory errors errorMetadataMap_[600] = ErrorMetadata( - ErrorSeverity::Critical, ErrorCategory::Memory, ErrorRecoveryStrategy::Restart, - "Memory allocation failed", "Restart application or free up system memory"); - - errorMetadataMap_[601] = ErrorMetadata( - ErrorSeverity::Fatal, ErrorCategory::Memory, ErrorRecoveryStrategy::Restart, - "Out of memory", "Close other applications or restart system"); - - errorMetadataMap_[607] = ErrorMetadata( - ErrorSeverity::Fatal, ErrorCategory::Memory, ErrorRecoveryStrategy::Restart, - "Stack overflow", "Reduce recursion depth or increase stack size"); - + ErrorSeverity::Critical, ErrorCategory::Memory, + ErrorRecoveryStrategy::Restart, "Memory allocation failed", + "Restart application or free up system memory"); + + errorMetadataMap_[601] = + ErrorMetadata(ErrorSeverity::Fatal, ErrorCategory::Memory, + ErrorRecoveryStrategy::Restart, "Out of memory", + "Close other applications or restart system"); + + errorMetadataMap_[607] = + ErrorMetadata(ErrorSeverity::Fatal, ErrorCategory::Memory, + ErrorRecoveryStrategy::Restart, "Stack overflow", + "Reduce recursion depth or increase stack size"); + // User input errors errorMetadataMap_[700] = ErrorMetadata( - ErrorSeverity::Warning, ErrorCategory::Validation, ErrorRecoveryStrategy::UserIntervention, - "Invalid input", "Please provide valid input according to the format requirements"); - + ErrorSeverity::Warning, ErrorCategory::Validation, + ErrorRecoveryStrategy::UserIntervention, "Invalid input", + "Please provide valid input according to the format requirements"); + errorMetadataMap_[701] = ErrorMetadata( - ErrorSeverity::Warning, ErrorCategory::Validation, ErrorRecoveryStrategy::UserIntervention, - "Input out of range", "Please provide input within the acceptable range"); - + ErrorSeverity::Warning, ErrorCategory::Validation, + ErrorRecoveryStrategy::UserIntervention, "Input out of range", + "Please provide input within the acceptable range"); + // Configuration errors - errorMetadataMap_[800] = ErrorMetadata( - ErrorSeverity::Error, ErrorCategory::Configuration, ErrorRecoveryStrategy::UserIntervention, - "Missing configuration file", "Create or restore the configuration file"); - + errorMetadataMap_[800] = + ErrorMetadata(ErrorSeverity::Error, ErrorCategory::Configuration, + ErrorRecoveryStrategy::UserIntervention, + "Missing configuration file", + "Create or restore the configuration file"); + errorMetadataMap_[801] = ErrorMetadata( - ErrorSeverity::Error, ErrorCategory::Configuration, ErrorRecoveryStrategy::UserIntervention, - "Invalid configuration", "Check and correct the configuration settings"); - + ErrorSeverity::Error, ErrorCategory::Configuration, + ErrorRecoveryStrategy::UserIntervention, "Invalid configuration", + "Check and correct the configuration settings"); + // Process errors - errorMetadataMap_[900] = ErrorMetadata( - ErrorSeverity::Error, ErrorCategory::System, ErrorRecoveryStrategy::Retry, - "Process not found", "Check if the process is running"); - - errorMetadataMap_[905] = ErrorMetadata( - ErrorSeverity::Critical, ErrorCategory::System, ErrorRecoveryStrategy::Restart, - "Deadlock detected", "Restart the application to resolve deadlock"); - + errorMetadataMap_[900] = + ErrorMetadata(ErrorSeverity::Error, ErrorCategory::System, + ErrorRecoveryStrategy::Retry, "Process not found", + "Check if the process is running"); + + errorMetadataMap_[905] = + ErrorMetadata(ErrorSeverity::Critical, ErrorCategory::System, + ErrorRecoveryStrategy::Restart, "Deadlock detected", + "Restart the application to resolve deadlock"); + // Server errors errorMetadataMap_[300] = ErrorMetadata( - ErrorSeverity::Error, ErrorCategory::Application, ErrorRecoveryStrategy::UserIntervention, - "Invalid parameters", "Check and correct the input parameters"); - + ErrorSeverity::Error, ErrorCategory::Application, + ErrorRecoveryStrategy::UserIntervention, "Invalid parameters", + "Check and correct the input parameters"); + errorMetadataMap_[322] = ErrorMetadata( - ErrorSeverity::Error, ErrorCategory::Security, ErrorRecoveryStrategy::UserIntervention, - "Authentication failed", "Check credentials and try again"); - - errorMetadataMap_[324] = ErrorMetadata( - ErrorSeverity::Warning, ErrorCategory::External, ErrorRecoveryStrategy::Retry, - "Server overload", "Wait and retry later when server load decreases"); + ErrorSeverity::Error, ErrorCategory::Security, + ErrorRecoveryStrategy::UserIntervention, "Authentication failed", + "Check credentials and try again"); + + errorMetadataMap_[324] = + ErrorMetadata(ErrorSeverity::Warning, ErrorCategory::External, + ErrorRecoveryStrategy::Retry, "Server overload", + "Wait and retry later when server load decreases"); }); } @@ -143,8 +170,9 @@ ErrorMetadata ErrorCodeMapper::getMetadata(int errorCode) { if (it != errorMetadataMap_.end()) { return it->second; } - return ErrorMetadata(ErrorSeverity::Error, ErrorCategory::Unknown, ErrorRecoveryStrategy::None, - "Unknown error", "Contact support for assistance"); + return ErrorMetadata(ErrorSeverity::Error, ErrorCategory::Unknown, + ErrorRecoveryStrategy::None, "Unknown error", + "Contact support for assistance"); } std::string ErrorCodeMapper::getDescription(int errorCode) { @@ -173,4 +201,4 @@ bool ErrorCodeMapper::isRetryable(int errorCode) { return strategy == ErrorRecoveryStrategy::Retry; } -} // namespace atom::error +} // namespace atom::error diff --git a/atom/error/error_code.hpp b/atom/error/error_code.hpp index b315849a..16e8c028 100644 --- a/atom/error/error_code.hpp +++ b/atom/error/error_code.hpp @@ -26,13 +26,13 @@ namespace atom::error { * @brief Error severity levels for categorizing error importance */ enum class ErrorSeverity : int { - Trace = 0, ///< Detailed trace information - Debug = 1, ///< Debug information - Info = 2, ///< Informational messages - Warning = 3, ///< Warning conditions - Error = 4, ///< Error conditions - Critical = 5, ///< Critical conditions - Fatal = 6 ///< Fatal conditions that may cause termination + Trace = 0, ///< Detailed trace information + Debug = 1, ///< Debug information + Info = 2, ///< Informational messages + Warning = 3, ///< Warning conditions + Error = 4, ///< Error conditions + Critical = 5, ///< Critical conditions + Fatal = 6 ///< Fatal conditions that may cause termination }; /** @@ -43,25 +43,25 @@ enum class ErrorCategory : int { System = 1, ///< System-level errors Application = 2, ///< Application-level errors Network = 3, ///< Network-related errors - IO = 4, ///< Input/Output errors - Memory = 5, ///< Memory-related errors - Security = 6, ///< Security-related errors - Configuration = 7, ///< Configuration errors - Validation = 8, ///< Data validation errors - Business = 9, ///< Business logic errors - External = 10 ///< External service errors + IO = 4, ///< Input/Output errors + Memory = 5, ///< Memory-related errors + Security = 6, ///< Security-related errors + Configuration = 7, ///< Configuration errors + Validation = 8, ///< Data validation errors + Business = 9, ///< Business logic errors + External = 10 ///< External service errors }; /** * @brief Error recovery strategies */ enum class ErrorRecoveryStrategy : int { - None = 0, ///< No recovery possible - Retry = 1, ///< Can be retried - Fallback = 2, ///< Has fallback mechanism - UserIntervention = 3, ///< Requires user intervention - Restart = 4, ///< Requires restart - Ignore = 5 ///< Can be safely ignored + None = 0, ///< No recovery possible + Retry = 1, ///< Can be retried + Fallback = 2, ///< Has fallback mechanism + UserIntervention = 3, ///< Requires user intervention + Restart = 4, ///< Requires restart + Ignore = 5 ///< Can be safely ignored }; // 基础错误码(可选) @@ -254,10 +254,14 @@ struct ErrorMetadata { int maxRetries = 3; ErrorMetadata() = default; - ErrorMetadata(ErrorSeverity sev, ErrorCategory cat, ErrorRecoveryStrategy rec, - std::string desc = "", std::string sol = "") - : severity(sev), category(cat), recovery(rec), - description(std::move(desc)), solution(std::move(sol)) {} + ErrorMetadata(ErrorSeverity sev, ErrorCategory cat, + ErrorRecoveryStrategy rec, std::string desc = "", + std::string sol = "") + : severity(sev), + category(cat), + recovery(rec), + description(std::move(desc)), + solution(std::move(sol)) {} }; /** @@ -310,14 +314,22 @@ class ErrorCodeMapper { */ constexpr std::string_view severityToString(ErrorSeverity severity) { switch (severity) { - case ErrorSeverity::Trace: return "TRACE"; - case ErrorSeverity::Debug: return "DEBUG"; - case ErrorSeverity::Info: return "INFO"; - case ErrorSeverity::Warning: return "WARNING"; - case ErrorSeverity::Error: return "ERROR"; - case ErrorSeverity::Critical: return "CRITICAL"; - case ErrorSeverity::Fatal: return "FATAL"; - default: return "UNKNOWN"; + case ErrorSeverity::Trace: + return "TRACE"; + case ErrorSeverity::Debug: + return "DEBUG"; + case ErrorSeverity::Info: + return "INFO"; + case ErrorSeverity::Warning: + return "WARNING"; + case ErrorSeverity::Error: + return "ERROR"; + case ErrorSeverity::Critical: + return "CRITICAL"; + case ErrorSeverity::Fatal: + return "FATAL"; + default: + return "UNKNOWN"; } } @@ -326,36 +338,56 @@ constexpr std::string_view severityToString(ErrorSeverity severity) { */ constexpr std::string_view categoryToString(ErrorCategory category) { switch (category) { - case ErrorCategory::Unknown: return "UNKNOWN"; - case ErrorCategory::System: return "SYSTEM"; - case ErrorCategory::Application: return "APPLICATION"; - case ErrorCategory::Network: return "NETWORK"; - case ErrorCategory::IO: return "IO"; - case ErrorCategory::Memory: return "MEMORY"; - case ErrorCategory::Security: return "SECURITY"; - case ErrorCategory::Configuration: return "CONFIGURATION"; - case ErrorCategory::Validation: return "VALIDATION"; - case ErrorCategory::Business: return "BUSINESS"; - case ErrorCategory::External: return "EXTERNAL"; - default: return "UNKNOWN"; + case ErrorCategory::Unknown: + return "UNKNOWN"; + case ErrorCategory::System: + return "SYSTEM"; + case ErrorCategory::Application: + return "APPLICATION"; + case ErrorCategory::Network: + return "NETWORK"; + case ErrorCategory::IO: + return "IO"; + case ErrorCategory::Memory: + return "MEMORY"; + case ErrorCategory::Security: + return "SECURITY"; + case ErrorCategory::Configuration: + return "CONFIGURATION"; + case ErrorCategory::Validation: + return "VALIDATION"; + case ErrorCategory::Business: + return "BUSINESS"; + case ErrorCategory::External: + return "EXTERNAL"; + default: + return "UNKNOWN"; } } /** * @brief Convert recovery strategy to string */ -constexpr std::string_view recoveryStrategyToString(ErrorRecoveryStrategy strategy) { +constexpr std::string_view recoveryStrategyToString( + ErrorRecoveryStrategy strategy) { switch (strategy) { - case ErrorRecoveryStrategy::None: return "NONE"; - case ErrorRecoveryStrategy::Retry: return "RETRY"; - case ErrorRecoveryStrategy::Fallback: return "FALLBACK"; - case ErrorRecoveryStrategy::UserIntervention: return "USER_INTERVENTION"; - case ErrorRecoveryStrategy::Restart: return "RESTART"; - case ErrorRecoveryStrategy::Ignore: return "IGNORE"; - default: return "UNKNOWN"; + case ErrorRecoveryStrategy::None: + return "NONE"; + case ErrorRecoveryStrategy::Retry: + return "RETRY"; + case ErrorRecoveryStrategy::Fallback: + return "FALLBACK"; + case ErrorRecoveryStrategy::UserIntervention: + return "USER_INTERVENTION"; + case ErrorRecoveryStrategy::Restart: + return "RESTART"; + case ErrorRecoveryStrategy::Ignore: + return "IGNORE"; + default: + return "UNKNOWN"; } } -} // namespace atom::error +} // namespace atom::error #endif diff --git a/atom/error/error_context.cpp b/atom/error/error_context.cpp index e241963d..fe434e2e 100644 --- a/atom/error/error_context.cpp +++ b/atom/error/error_context.cpp @@ -13,18 +13,18 @@ Description: Implementation of error context system **************************************************/ #include "error_context.hpp" -#include -#include -#include #include +#include +#include #include +#include #ifdef _WIN32 -#include #include +#include #else -#include #include +#include #endif namespace atom::error { @@ -33,7 +33,7 @@ ErrorId generateErrorId() { static std::random_device rd; static std::mt19937 gen(rd()); static std::uniform_int_distribution<> dis(0, 15); - + std::stringstream ss; ss << std::hex; for (int i = 0; i < 32; ++i) { @@ -46,52 +46,50 @@ ErrorId generateErrorId() { } ErrorContext::ErrorContext(int errorCode, std::string message) - : errorId_(generateErrorId()) - , errorCode_(errorCode) - , message_(std::move(message)) - , timestamp_(std::chrono::system_clock::now()) - , threadId_(std::this_thread::get_id()) - , metadata_(ErrorCodeMapper::getMetadata(errorCode)) - , retryCount_(0) - , maxRetries_(metadata_.maxRetries) { + : errorId_(generateErrorId()), + errorCode_(errorCode), + message_(std::move(message)), + timestamp_(std::chrono::system_clock::now()), + threadId_(std::this_thread::get_id()), + metadata_(ErrorCodeMapper::getMetadata(errorCode)), + retryCount_(0), + maxRetries_(metadata_.maxRetries) { initializeSystemInfo(); } ErrorContext::ErrorContext(const ErrorContext& other) - : errorId_(other.errorId_) - , errorCode_(other.errorCode_) - , message_(other.message_) - , timestamp_(other.timestamp_) - , threadId_(other.threadId_) - , metadata_(other.metadata_) - , userData_(other.userData_) - , systemInfo_(other.systemInfo_) - , tags_(other.tags_) - , correlationId_(other.correlationId_) - , parentErrorId_(other.parentErrorId_) - , childErrorIds_(other.childErrorIds_) - , retryCount_(other.retryCount_) - , maxRetries_(other.maxRetries_) - , stackTrace_(other.stackTrace_) { -} + : errorId_(other.errorId_), + errorCode_(other.errorCode_), + message_(other.message_), + timestamp_(other.timestamp_), + threadId_(other.threadId_), + metadata_(other.metadata_), + userData_(other.userData_), + systemInfo_(other.systemInfo_), + tags_(other.tags_), + correlationId_(other.correlationId_), + parentErrorId_(other.parentErrorId_), + childErrorIds_(other.childErrorIds_), + retryCount_(other.retryCount_), + maxRetries_(other.maxRetries_), + stackTrace_(other.stackTrace_) {} ErrorContext::ErrorContext(ErrorContext&& other) noexcept - : errorId_(std::move(other.errorId_)) - , errorCode_(other.errorCode_) - , message_(std::move(other.message_)) - , timestamp_(other.timestamp_) - , threadId_(other.threadId_) - , metadata_(std::move(other.metadata_)) - , userData_(std::move(other.userData_)) - , systemInfo_(std::move(other.systemInfo_)) - , tags_(std::move(other.tags_)) - , correlationId_(std::move(other.correlationId_)) - , parentErrorId_(std::move(other.parentErrorId_)) - , childErrorIds_(std::move(other.childErrorIds_)) - , retryCount_(other.retryCount_) - , maxRetries_(other.maxRetries_) - , stackTrace_(std::move(other.stackTrace_)) { -} + : errorId_(std::move(other.errorId_)), + errorCode_(other.errorCode_), + message_(std::move(other.message_)), + timestamp_(other.timestamp_), + threadId_(other.threadId_), + metadata_(std::move(other.metadata_)), + userData_(std::move(other.userData_)), + systemInfo_(std::move(other.systemInfo_)), + tags_(std::move(other.tags_)), + correlationId_(std::move(other.correlationId_)), + parentErrorId_(std::move(other.parentErrorId_)), + childErrorIds_(std::move(other.childErrorIds_)), + retryCount_(other.retryCount_), + maxRetries_(other.maxRetries_), + stackTrace_(std::move(other.stackTrace_)) {} ErrorContext& ErrorContext::operator=(const ErrorContext& other) { if (this != &other) { @@ -137,7 +135,8 @@ ErrorContext& ErrorContext::operator=(ErrorContext&& other) noexcept { return *this; } -auto ErrorContext::setUserData(const std::string& key, std::any value) -> ErrorContext& { +auto ErrorContext::setUserData(const std::string& key, + std::any value) -> ErrorContext& { std::lock_guard lock(mutex_); userData_[key] = std::move(value); return *this; @@ -154,7 +153,8 @@ auto ErrorContext::hasUserData(const std::string& key) const -> bool { return userData_.find(key) != userData_.end(); } -auto ErrorContext::setSystemInfo(const std::string& key, std::string value) -> ErrorContext& { +auto ErrorContext::setSystemInfo(const std::string& key, + std::string value) -> ErrorContext& { std::lock_guard lock(mutex_); systemInfo_[key] = std::move(value); return *this; @@ -183,7 +183,8 @@ auto ErrorContext::hasTag(const std::string& tag) const -> bool { return std::find(tags_.begin(), tags_.end(), tag) != tags_.end(); } -auto ErrorContext::setCorrelationId(const std::string& correlationId) -> ErrorContext& { +auto ErrorContext::setCorrelationId(const std::string& correlationId) + -> ErrorContext& { std::lock_guard lock(mutex_); correlationId_ = correlationId; return *this; @@ -219,9 +220,7 @@ auto ErrorContext::incrementRetryCount() -> ErrorContext& { return *this; } -auto ErrorContext::getRetryCount() const -> int { - return retryCount_; -} +auto ErrorContext::getRetryCount() const -> int { return retryCount_; } auto ErrorContext::setMaxRetries(int maxRetries) -> ErrorContext& { std::lock_guard lock(mutex_); @@ -229,16 +228,15 @@ auto ErrorContext::setMaxRetries(int maxRetries) -> ErrorContext& { return *this; } -auto ErrorContext::getMaxRetries() const -> int { - return maxRetries_; -} +auto ErrorContext::getMaxRetries() const -> int { return maxRetries_; } auto ErrorContext::canRetry() const -> bool { - return retryCount_ < maxRetries_ && + return retryCount_ < maxRetries_ && (metadata_.recovery == ErrorRecoveryStrategy::Retry); } -auto ErrorContext::setStackTrace(const std::string& stackTrace) -> ErrorContext& { +auto ErrorContext::setStackTrace(const std::string& stackTrace) + -> ErrorContext& { std::lock_guard lock(mutex_); stackTrace_ = stackTrace; return *this; @@ -249,43 +247,45 @@ auto ErrorContext::getStackTrace() const -> const std::string& { } void ErrorContext::initializeSystemInfo() { - // Get process ID - #ifdef _WIN32 +// Get process ID +#ifdef _WIN32 systemInfo_["pid"] = std::to_string(GetCurrentProcessId()); - #else +#else systemInfo_["pid"] = std::to_string(getpid()); - #endif - - // Get system information - #ifdef _WIN32 +#endif + +// Get system information +#ifdef _WIN32 SYSTEM_INFO sysInfo; GetSystemInfo(&sysInfo); systemInfo_["cpu_count"] = std::to_string(sysInfo.dwNumberOfProcessors); - #else +#else systemInfo_["cpu_count"] = std::to_string(sysconf(_SC_NPROCESSORS_ONLN)); - + struct utsname unameData; if (uname(&unameData) == 0) { systemInfo_["os_name"] = unameData.sysname; systemInfo_["os_version"] = unameData.release; systemInfo_["hostname"] = unameData.nodename; } - #endif - +#endif + // Thread ID as string std::stringstream ss; ss << threadId_; systemInfo_["thread_id"] = ss.str(); } -auto ErrorContext::create(int errorCode, const std::string& message) -> std::shared_ptr { +auto ErrorContext::create(int errorCode, const std::string& message) + -> std::shared_ptr { auto context = std::make_shared(errorCode, message); ErrorContextManager::getInstance().registerContext(context); return context; } -auto ErrorContext::createWithCorrelation(int errorCode, const std::string& correlationId, - const std::string& message) -> std::shared_ptr { +auto ErrorContext::createWithCorrelation( + int errorCode, const std::string& correlationId, + const std::string& message) -> std::shared_ptr { auto context = create(errorCode, message); context->setCorrelationId(correlationId); return context; @@ -298,19 +298,24 @@ auto ErrorContext::toJson() const -> std::string { ss << " \"errorId\": \"" << errorId_ << "\",\n"; ss << " \"errorCode\": " << errorCode_ << ",\n"; ss << " \"message\": \"" << message_ << "\",\n"; - ss << " \"severity\": \"" << severityToString(metadata_.severity) << "\",\n"; - ss << " \"category\": \"" << categoryToString(metadata_.category) << "\",\n"; - ss << " \"recovery\": \"" << recoveryStrategyToString(metadata_.recovery) << "\",\n"; + ss << " \"severity\": \"" << severityToString(metadata_.severity) + << "\",\n"; + ss << " \"category\": \"" << categoryToString(metadata_.category) + << "\",\n"; + ss << " \"recovery\": \"" << recoveryStrategyToString(metadata_.recovery) + << "\",\n"; // Timestamp auto time_t = std::chrono::system_clock::to_time_t(timestamp_); - ss << " \"timestamp\": \"" << std::put_time(std::gmtime(&time_t), "%Y-%m-%dT%H:%M:%SZ") << "\",\n"; + ss << " \"timestamp\": \"" + << std::put_time(std::gmtime(&time_t), "%Y-%m-%dT%H:%M:%SZ") << "\",\n"; // System info ss << " \"systemInfo\": {\n"; bool first = true; for (const auto& [key, value] : systemInfo_) { - if (!first) ss << ",\n"; + if (!first) + ss << ",\n"; ss << " \"" << key << "\": \"" << value << "\""; first = false; } @@ -320,7 +325,8 @@ auto ErrorContext::toJson() const -> std::string { ss << " \"tags\": ["; first = true; for (const auto& tag : tags_) { - if (!first) ss << ", "; + if (!first) + ss << ", "; ss << "\"" << tag << "\""; first = false; } @@ -344,10 +350,12 @@ auto ErrorContext::toString() const -> std::string { ss << " Message: " << message_ << "\n"; ss << " Severity: " << severityToString(metadata_.severity) << "\n"; ss << " Category: " << categoryToString(metadata_.category) << "\n"; - ss << " Recovery: " << recoveryStrategyToString(metadata_.recovery) << "\n"; + ss << " Recovery: " << recoveryStrategyToString(metadata_.recovery) + << "\n"; auto time_t = std::chrono::system_clock::to_time_t(timestamp_); - ss << " Timestamp: " << std::put_time(std::localtime(&time_t), "%Y-%m-%d %H:%M:%S") << "\n"; + ss << " Timestamp: " + << std::put_time(std::localtime(&time_t), "%Y-%m-%d %H:%M:%S") << "\n"; if (!correlationId_.empty()) { ss << " Correlation ID: " << correlationId_ << "\n"; @@ -360,7 +368,8 @@ auto ErrorContext::toString() const -> std::string { if (!tags_.empty()) { ss << " Tags: "; for (size_t i = 0; i < tags_.size(); ++i) { - if (i > 0) ss << ", "; + if (i > 0) + ss << ", "; ss << tags_[i]; } ss << "\n"; @@ -381,20 +390,24 @@ auto ErrorContextManager::getInstance() -> ErrorContextManager& { return instance; } -void ErrorContextManager::registerContext(std::shared_ptr context) { - if (!context) return; +void ErrorContextManager::registerContext( + std::shared_ptr context) { + if (!context) + return; std::unique_lock lock(contextsMutex_); contexts_[context->getErrorId()] = context; } -auto ErrorContextManager::getContext(const ErrorId& errorId) const -> std::shared_ptr { +auto ErrorContextManager::getContext(const ErrorId& errorId) const + -> std::shared_ptr { std::shared_lock lock(contextsMutex_); auto it = contexts_.find(errorId); return it != contexts_.end() ? it->second : nullptr; } -auto ErrorContextManager::getContextsByCorrelation(const std::string& correlationId) const +auto ErrorContextManager::getContextsByCorrelation( + const std::string& correlationId) const -> std::vector> { std::shared_lock lock(contextsMutex_); std::vector> result; @@ -408,7 +421,8 @@ auto ErrorContextManager::getContextsByCorrelation(const std::string& correlatio return result; } -auto ErrorContextManager::getStatistics() const -> std::unordered_map { +auto ErrorContextManager::getStatistics() const + -> std::unordered_map { std::shared_lock lock(contextsMutex_); std::unordered_map stats; @@ -456,8 +470,7 @@ void ErrorContextManager::clear() { // ScopedErrorContext implementation ScopedErrorContext::ScopedErrorContext(std::shared_ptr context) - : context_(std::move(context)) { -} + : context_(std::move(context)) {} ScopedErrorContext::~ScopedErrorContext() { // Context is automatically managed by shared_ptr @@ -467,4 +480,4 @@ auto ScopedErrorContext::getContext() const -> std::shared_ptr { return context_; } -} // namespace atom::error +} // namespace atom::error diff --git a/atom/error/error_context.hpp b/atom/error/error_context.hpp index 6d8454ce..d1a2269f 100644 --- a/atom/error/error_context.hpp +++ b/atom/error/error_context.hpp @@ -15,15 +15,15 @@ Description: Error context system for capturing additional debugging information #ifndef ATOM_ERROR_CONTEXT_HPP #define ATOM_ERROR_CONTEXT_HPP +#include #include #include +#include +#include #include +#include #include #include -#include -#include -#include -#include #include "../macro.hpp" #include "error_code.hpp" @@ -49,27 +49,27 @@ class ErrorContext { * @brief Constructor with basic error information */ ErrorContext(int errorCode, std::string message = ""); - + /** * @brief Copy constructor */ ErrorContext(const ErrorContext& other); - + /** * @brief Move constructor */ ErrorContext(ErrorContext&& other) noexcept; - + /** * @brief Assignment operator */ ErrorContext& operator=(const ErrorContext& other); - + /** * @brief Move assignment operator */ ErrorContext& operator=(ErrorContext&& other) noexcept; - + /** * @brief Destructor */ @@ -78,57 +78,76 @@ class ErrorContext { // Basic error information [[nodiscard]] auto getErrorId() const -> const ErrorId& { return errorId_; } [[nodiscard]] auto getErrorCode() const -> int { return errorCode_; } - [[nodiscard]] auto getMessage() const -> const std::string& { return message_; } - [[nodiscard]] auto getTimestamp() const -> std::chrono::system_clock::time_point { return timestamp_; } - [[nodiscard]] auto getThreadId() const -> std::thread::id { return threadId_; } - + [[nodiscard]] auto getMessage() const -> const std::string& { + return message_; + } + [[nodiscard]] auto getTimestamp() const + -> std::chrono::system_clock::time_point { + return timestamp_; + } + [[nodiscard]] auto getThreadId() const -> std::thread::id { + return threadId_; + } + // Error metadata - [[nodiscard]] auto getSeverity() const -> ErrorSeverity { return metadata_.severity; } - [[nodiscard]] auto getCategory() const -> ErrorCategory { return metadata_.category; } - [[nodiscard]] auto getRecoveryStrategy() const -> ErrorRecoveryStrategy { return metadata_.recovery; } - [[nodiscard]] auto getMetadata() const -> const ErrorMetadata& { return metadata_; } - + [[nodiscard]] auto getSeverity() const -> ErrorSeverity { + return metadata_.severity; + } + [[nodiscard]] auto getCategory() const -> ErrorCategory { + return metadata_.category; + } + [[nodiscard]] auto getRecoveryStrategy() const -> ErrorRecoveryStrategy { + return metadata_.recovery; + } + [[nodiscard]] auto getMetadata() const -> const ErrorMetadata& { + return metadata_; + } + // Context information auto setUserData(const std::string& key, std::any value) -> ErrorContext&; [[nodiscard]] auto getUserData(const std::string& key) const -> std::any; [[nodiscard]] auto hasUserData(const std::string& key) const -> bool; - - auto setSystemInfo(const std::string& key, std::string value) -> ErrorContext&; - [[nodiscard]] auto getSystemInfo(const std::string& key) const -> std::string; - + + auto setSystemInfo(const std::string& key, + std::string value) -> ErrorContext&; + [[nodiscard]] auto getSystemInfo(const std::string& key) const + -> std::string; + auto addTag(const std::string& tag) -> ErrorContext&; [[nodiscard]] auto getTags() const -> const std::vector&; [[nodiscard]] auto hasTag(const std::string& tag) const -> bool; - + // Error correlation auto setCorrelationId(const std::string& correlationId) -> ErrorContext&; [[nodiscard]] auto getCorrelationId() const -> const std::string&; - + auto setParentErrorId(const ErrorId& parentId) -> ErrorContext&; [[nodiscard]] auto getParentErrorId() const -> const ErrorId&; - + auto addChildErrorId(const ErrorId& childId) -> ErrorContext&; [[nodiscard]] auto getChildErrorIds() const -> const std::vector&; - + // Retry information auto incrementRetryCount() -> ErrorContext&; [[nodiscard]] auto getRetryCount() const -> int; auto setMaxRetries(int maxRetries) -> ErrorContext&; [[nodiscard]] auto getMaxRetries() const -> int; [[nodiscard]] auto canRetry() const -> bool; - + // Stack trace integration auto setStackTrace(const std::string& stackTrace) -> ErrorContext&; [[nodiscard]] auto getStackTrace() const -> const std::string&; - + // Serialization [[nodiscard]] auto toJson() const -> std::string; [[nodiscard]] auto toString() const -> std::string; - + // Static factory methods - static auto create(int errorCode, const std::string& message = "") -> std::shared_ptr; - static auto createWithCorrelation(int errorCode, const std::string& correlationId, - const std::string& message = "") -> std::shared_ptr; + static auto create(int errorCode, const std::string& message = "") + -> std::shared_ptr; + static auto createWithCorrelation( + int errorCode, const std::string& correlationId, + const std::string& message = "") -> std::shared_ptr; private: ErrorId errorId_; @@ -137,32 +156,33 @@ class ErrorContext { std::chrono::system_clock::time_point timestamp_; std::thread::id threadId_; ErrorMetadata metadata_; - + // Context data std::unordered_map userData_; std::unordered_map systemInfo_; std::vector tags_; - + // Error correlation std::string correlationId_; ErrorId parentErrorId_; std::vector childErrorIds_; - + // Retry information int retryCount_; int maxRetries_; - + // Stack trace std::string stackTrace_; - + // Thread safety mutable std::mutex mutex_; - + void initializeSystemInfo(); }; /** - * @brief Error context manager for tracking error contexts across the application + * @brief Error context manager for tracking error contexts across the + * application */ class ErrorContextManager { public: @@ -170,33 +190,36 @@ class ErrorContextManager { * @brief Get the singleton instance */ static auto getInstance() -> ErrorContextManager&; - + /** * @brief Register an error context */ void registerContext(std::shared_ptr context); - + /** * @brief Get error context by ID */ - [[nodiscard]] auto getContext(const ErrorId& errorId) const -> std::shared_ptr; - + [[nodiscard]] auto getContext(const ErrorId& errorId) const + -> std::shared_ptr; + /** * @brief Get all contexts with a specific correlation ID */ - [[nodiscard]] auto getContextsByCorrelation(const std::string& correlationId) const + [[nodiscard]] auto getContextsByCorrelation( + const std::string& correlationId) const -> std::vector>; - + /** * @brief Get error context statistics */ - [[nodiscard]] auto getStatistics() const -> std::unordered_map; - + [[nodiscard]] auto getStatistics() const + -> std::unordered_map; + /** * @brief Clear old error contexts (cleanup) */ void cleanup(std::chrono::minutes maxAge = std::chrono::minutes(60)); - + /** * @brief Clear all error contexts */ @@ -207,7 +230,7 @@ class ErrorContextManager { ~ErrorContextManager() = default; ErrorContextManager(const ErrorContextManager&) = delete; ErrorContextManager& operator=(const ErrorContextManager&) = delete; - + std::unordered_map> contexts_; mutable std::shared_mutex contextsMutex_; }; @@ -219,7 +242,7 @@ class ScopedErrorContext { public: explicit ScopedErrorContext(std::shared_ptr context); ~ScopedErrorContext(); - + [[nodiscard]] auto getContext() const -> std::shared_ptr; private: @@ -229,18 +252,19 @@ class ScopedErrorContext { /** * @brief Macro for creating error context with current location */ -#define CREATE_ERROR_CONTEXT(errorCode, message) \ - atom::error::ErrorContext::create(errorCode, message) \ - ->setSystemInfo("file", ATOM_FILE_NAME) \ +#define CREATE_ERROR_CONTEXT(errorCode, message) \ + atom::error::ErrorContext::create(errorCode, message) \ + ->setSystemInfo("file", ATOM_FILE_NAME) \ ->setSystemInfo("line", std::to_string(ATOM_FILE_LINE)) \ ->setSystemInfo("function", ATOM_FUNC_NAME) /** * @brief Macro for creating scoped error context */ -#define SCOPED_ERROR_CONTEXT(errorCode, message) \ - atom::error::ScopedErrorContext scopedContext(CREATE_ERROR_CONTEXT(errorCode, message)) +#define SCOPED_ERROR_CONTEXT(errorCode, message) \ + atom::error::ScopedErrorContext scopedContext( \ + CREATE_ERROR_CONTEXT(errorCode, message)) -} // namespace atom::error +} // namespace atom::error -#endif // ATOM_ERROR_CONTEXT_HPP +#endif // ATOM_ERROR_CONTEXT_HPP diff --git a/atom/error/error_formatter.cpp b/atom/error/error_formatter.cpp index eb206527..0be41843 100644 --- a/atom/error/error_formatter.cpp +++ b/atom/error/error_formatter.cpp @@ -14,18 +14,20 @@ Description: Implementation of error formatting system #include "error_formatter.hpp" #include +#include #include #include #include -#include namespace atom::error { // ErrorFormatter base implementation -std::string ErrorFormatter::formatMultiple(const std::vector>& contexts) { +std::string ErrorFormatter::formatMultiple( + const std::vector>& contexts) { std::stringstream ss; for (size_t i = 0; i < contexts.size(); ++i) { - if (i > 0) ss << "\n"; + if (i > 0) + ss << "\n"; ss << format(contexts[i]); } return ss.str(); @@ -33,10 +35,10 @@ std::string ErrorFormatter::formatMultiple(const std::vector context) { - if (!context) return ""; - + if (!context) + return ""; + std::stringstream ss; - + // Header ss << "=== ERROR REPORT ===\n"; - + // Basic information ss << "Error ID: " << context->getErrorId() << "\n"; ss << "Error Code: " << context->getErrorCode() << "\n"; ss << "Message: " << context->getMessage() << "\n"; ss << "Severity: " << severityToString(context->getSeverity()) << "\n"; ss << "Category: " << categoryToString(context->getCategory()) << "\n"; - + // Timestamp if (includeTimestamp_) { - auto time_t = std::chrono::system_clock::to_time_t(context->getTimestamp()); - ss << "Timestamp: " << std::put_time(std::localtime(&time_t), dateFormat_.c_str()) << "\n"; + auto time_t = + std::chrono::system_clock::to_time_t(context->getTimestamp()); + ss << "Timestamp: " + << std::put_time(std::localtime(&time_t), dateFormat_.c_str()) + << "\n"; } - + // Correlation information if (!context->getCorrelationId().empty()) { ss << "Correlation ID: " << context->getCorrelationId() << "\n"; } - + // Retry information - ss << "Retry Count: " << context->getRetryCount() << "/" << context->getMaxRetries() << "\n"; - + ss << "Retry Count: " << context->getRetryCount() << "/" + << context->getMaxRetries() << "\n"; + // Tags const auto& tags = context->getTags(); if (!tags.empty()) { ss << "Tags: "; for (size_t i = 0; i < tags.size(); ++i) { - if (i > 0) ss << ", "; + if (i > 0) + ss << ", "; ss << tags[i]; } ss << "\n"; } - + // System information if (includeSystemInfo_) { ss << "\n--- System Information ---\n"; auto sysInfo = context->getSystemInfo("pid"); - if (!sysInfo.empty()) ss << "Process ID: " << sysInfo << "\n"; - + if (!sysInfo.empty()) + ss << "Process ID: " << sysInfo << "\n"; + sysInfo = context->getSystemInfo("thread_id"); - if (!sysInfo.empty()) ss << "Thread ID: " << sysInfo << "\n"; - + if (!sysInfo.empty()) + ss << "Thread ID: " << sysInfo << "\n"; + sysInfo = context->getSystemInfo("hostname"); - if (!sysInfo.empty()) ss << "Hostname: " << sysInfo << "\n"; + if (!sysInfo.empty()) + ss << "Hostname: " << sysInfo << "\n"; } - + // Stack trace if (includeStackTrace_ && !context->getStackTrace().empty()) { ss << "\n--- Stack Trace ---\n"; ss << context->getStackTrace() << "\n"; } - + ss << "==================\n"; - + return ss.str(); } -void PlainTextFormatter::setOption(const std::string& key, const std::string& value) { +void PlainTextFormatter::setOption(const std::string& key, + const std::string& value) { options_[key] = value; - + if (key == "include_stack_trace") { includeStackTrace_ = (value == "true"); } else if (key == "include_system_info") { @@ -127,71 +139,94 @@ std::string PlainTextFormatter::getOption(const std::string& key) const { } // JsonFormatter implementation -JsonFormatter::JsonFormatter() - : prettyPrint_(true) - , indentSize_(2) { +JsonFormatter::JsonFormatter() : prettyPrint_(true), indentSize_(2) { options_["pretty_print"] = "true"; options_["indent_size"] = "2"; } std::string JsonFormatter::format(std::shared_ptr context) { - if (!context) return "{}"; - + if (!context) + return "{}"; + std::stringstream ss; std::string indent = prettyPrint_ ? std::string(indentSize_, ' ') : ""; std::string newline = prettyPrint_ ? "\n" : ""; - + ss << "{" << newline; - + // Basic information - ss << indent << formatJsonValue("errorId", context->getErrorId()) << newline; - ss << indent << formatJsonValue("errorCode", std::to_string(context->getErrorCode())) << newline; - ss << indent << formatJsonValue("message", context->getMessage()) << newline; - ss << indent << formatJsonValue("severity", std::string(severityToString(context->getSeverity()))) << newline; - ss << indent << formatJsonValue("category", std::string(categoryToString(context->getCategory()))) << newline; - + ss << indent << formatJsonValue("errorId", context->getErrorId()) + << newline; + ss << indent + << formatJsonValue("errorCode", std::to_string(context->getErrorCode())) + << newline; + ss << indent << formatJsonValue("message", context->getMessage()) + << newline; + ss << indent + << formatJsonValue("severity", + std::string(severityToString(context->getSeverity()))) + << newline; + ss << indent + << formatJsonValue("category", + std::string(categoryToString(context->getCategory()))) + << newline; + // Timestamp auto time_t = std::chrono::system_clock::to_time_t(context->getTimestamp()); - ss << indent << formatJsonValue("timestamp", std::to_string(time_t)) << newline; - + ss << indent << formatJsonValue("timestamp", std::to_string(time_t)) + << newline; + // Correlation ID if (!context->getCorrelationId().empty()) { - ss << indent << formatJsonValue("correlationId", context->getCorrelationId()) << newline; + ss << indent + << formatJsonValue("correlationId", context->getCorrelationId()) + << newline; } - + // Retry information - ss << indent << formatJsonValue("retryCount", std::to_string(context->getRetryCount())) << newline; - ss << indent << formatJsonValue("maxRetries", std::to_string(context->getMaxRetries())) << newline; - + ss << indent + << formatJsonValue("retryCount", + std::to_string(context->getRetryCount())) + << newline; + ss << indent + << formatJsonValue("maxRetries", + std::to_string(context->getMaxRetries())) + << newline; + // Tags const auto& tags = context->getTags(); if (!tags.empty()) { ss << indent << "\"tags\": ["; for (size_t i = 0; i < tags.size(); ++i) { - if (i > 0) ss << ", "; + if (i > 0) + ss << ", "; ss << "\"" << escapeJsonString(tags[i]) << "\""; } ss << "]," << newline; } - + // Stack trace if (!context->getStackTrace().empty()) { - ss << indent << formatJsonValue("stackTrace", context->getStackTrace(), true) << newline; + ss << indent + << formatJsonValue("stackTrace", context->getStackTrace(), true) + << newline; } - + ss << "}"; - + return ss.str(); } -std::string JsonFormatter::formatMultiple(const std::vector>& contexts) { +std::string JsonFormatter::formatMultiple( + const std::vector>& contexts) { std::stringstream ss; std::string newline = prettyPrint_ ? "\n" : ""; std::string indent = prettyPrint_ ? std::string(indentSize_, ' ') : ""; - + ss << "[" << newline; for (size_t i = 0; i < contexts.size(); ++i) { - if (i > 0) ss << "," << newline; + if (i > 0) + ss << "," << newline; if (prettyPrint_) { std::string contextJson = format(contexts[i]); // Add indentation to each line @@ -203,13 +238,14 @@ std::string JsonFormatter::formatMultiple(const std::vector context) { - if (!context) return ""; - + if (!context) + return ""; + std::stringstream ss; - + // Colored header based on severity Color headerColor = getSeverityColor(context->getSeverity()); ss << colorize("=== ERROR REPORT ===", headerColor) << "\n"; - + // Basic information with colors ss << "Error ID: " << colorize(context->getErrorId(), Color::Cyan) << "\n"; - ss << "Error Code: " << colorize(std::to_string(context->getErrorCode()), Color::Yellow) << "\n"; + ss << "Error Code: " + << colorize(std::to_string(context->getErrorCode()), Color::Yellow) + << "\n"; ss << "Message: " << colorize(context->getMessage(), Color::White) << "\n"; - ss << "Severity: " << colorize(std::string(severityToString(context->getSeverity())), headerColor) << "\n"; - ss << "Category: " << colorize(std::string(categoryToString(context->getCategory())), Color::Blue) << "\n"; - + ss << "Severity: " + << colorize(std::string(severityToString(context->getSeverity())), + headerColor) + << "\n"; + ss << "Category: " + << colorize(std::string(categoryToString(context->getCategory())), + Color::Blue) + << "\n"; + // Timestamp auto time_t = std::chrono::system_clock::to_time_t(context->getTimestamp()); - ss << "Timestamp: " << colorize(std::to_string(time_t), Color::Green) << "\n"; - + ss << "Timestamp: " << colorize(std::to_string(time_t), Color::Green) + << "\n"; + // Correlation information if (!context->getCorrelationId().empty()) { - ss << "Correlation ID: " << colorize(context->getCorrelationId(), Color::Magenta) << "\n"; + ss << "Correlation ID: " + << colorize(context->getCorrelationId(), Color::Magenta) << "\n"; } - + ss << colorize("==================", headerColor) << "\n"; - + return ss.str(); } -void ColoredFormatter::setOption(const std::string& key, const std::string& value) { +void ColoredFormatter::setOption(const std::string& key, + const std::string& value) { options_[key] = value; - + if (key == "enable_colors") { enableColors_ = (value == "true"); } @@ -317,11 +382,14 @@ std::string ColoredFormatter::getOption(const std::string& key) const { return it != options_.end() ? it->second : ""; } -std::string ColoredFormatter::colorize(const std::string& text, Color color) const { - if (!enableColors_) return text; - +std::string ColoredFormatter::colorize(const std::string& text, + Color color) const { + if (!enableColors_) + return text; + std::stringstream ss; - ss << "\033[" << static_cast(color) << "m" << text << "\033[" << static_cast(Color::Reset) << "m"; + ss << "\033[" << static_cast(color) << "m" << text << "\033[" + << static_cast(Color::Reset) << "m"; return ss.str(); } @@ -331,9 +399,12 @@ Color ColoredFormatter::getSeverityColor(ErrorSeverity severity) const { } // ErrorFormatterFactory implementation -std::unordered_map()>> ErrorFormatterFactory::customFormatters_; +std::unordered_map()>> + ErrorFormatterFactory::customFormatters_; -std::unique_ptr ErrorFormatterFactory::createFormatter(OutputFormat format) { +std::unique_ptr ErrorFormatterFactory::createFormatter( + OutputFormat format) { switch (format) { case OutputFormat::Plain: return std::make_unique(); @@ -350,11 +421,14 @@ std::unique_ptr ErrorFormatterFactory::createFormatter(OutputFor } } -void ErrorFormatterFactory::registerFormatter(const std::string& name, std::function()> factory) { +void ErrorFormatterFactory::registerFormatter( + const std::string& name, + std::function()> factory) { customFormatters_[name] = std::move(factory); } -std::unique_ptr ErrorFormatterFactory::createCustomFormatter(const std::string& name) { +std::unique_ptr ErrorFormatterFactory::createCustomFormatter( + const std::string& name) { auto it = customFormatters_.find(name); if (it != customFormatters_.end()) { return it->second(); @@ -363,7 +437,8 @@ std::unique_ptr ErrorFormatterFactory::createCustomFormatter(con } std::vector ErrorFormatterFactory::getAvailableFormatters() { - std::vector formatters = {"plain", "json", "colored", "html", "structured"}; + std::vector formatters = {"plain", "json", "colored", "html", + "structured"}; for (const auto& [name, factory] : customFormatters_) { formatters.push_back(name); @@ -373,8 +448,7 @@ std::vector ErrorFormatterFactory::getAvailableFormatters() { } // HtmlFormatter implementation -HtmlFormatter::HtmlFormatter() - : includeCSS_(true) { +HtmlFormatter::HtmlFormatter() : includeCSS_(true) { options_["include_css"] = "true"; cssStyle_ = R"( @@ -396,7 +470,8 @@ HtmlFormatter::HtmlFormatter() } std::string HtmlFormatter::format(std::shared_ptr context) { - if (!context) return ""; + if (!context) + return ""; std::stringstream ss; @@ -413,34 +488,40 @@ std::string HtmlFormatter::format(std::shared_ptr context) { // Basic information ss << "
\n"; ss << " Error ID:\n"; - ss << " " << escapeHtml(context->getErrorId()) << "\n"; + ss << " " + << escapeHtml(context->getErrorId()) << "\n"; ss << "
\n"; ss << "
\n"; ss << " Error Code:\n"; - ss << " " << context->getErrorCode() << "\n"; + ss << " " << context->getErrorCode() + << "\n"; ss << "
\n"; ss << "
\n"; ss << " Message:\n"; - ss << " " << escapeHtml(context->getMessage()) << "\n"; + ss << " " + << escapeHtml(context->getMessage()) << "\n"; ss << "
\n"; ss << "
\n"; ss << " Severity:\n"; - ss << " " << severityToString(context->getSeverity()) << "\n"; + ss << " " + << severityToString(context->getSeverity()) << "\n"; ss << "
\n"; ss << "
\n"; ss << " Category:\n"; - ss << " " << categoryToString(context->getCategory()) << "\n"; + ss << " " + << categoryToString(context->getCategory()) << "\n"; ss << "
\n"; // Stack trace if (!context->getStackTrace().empty()) { ss << "
\n"; ss << " Stack Trace:\n"; - ss << "
" << escapeHtml(context->getStackTrace()) << "
\n"; + ss << "
"
+           << escapeHtml(context->getStackTrace()) << "
\n"; ss << "
\n"; } @@ -449,7 +530,8 @@ std::string HtmlFormatter::format(std::shared_ptr context) { return ss.str(); } -std::string HtmlFormatter::formatMultiple(const std::vector>& contexts) { +std::string HtmlFormatter::formatMultiple( + const std::vector>& contexts) { std::stringstream ss; if (includeCSS_) { @@ -471,7 +553,8 @@ std::string HtmlFormatter::formatMultiple(const std::vector': escaped += ">"; break; - case '&': escaped += "&"; break; - case '"': escaped += """; break; - case '\'': escaped += "'"; break; - default: escaped += c; break; + case '<': + escaped += "<"; + break; + case '>': + escaped += ">"; + break; + case '&': + escaped += "&"; + break; + case '"': + escaped += """; + break; + case '\'': + escaped += "'"; + break; + default: + escaped += c; + break; } } @@ -506,48 +601,64 @@ std::string HtmlFormatter::escapeHtml(const std::string& str) const { std::string HtmlFormatter::getSeverityClass(ErrorSeverity severity) const { switch (severity) { - case ErrorSeverity::Trace: return "error-trace"; - case ErrorSeverity::Debug: return "error-debug"; - case ErrorSeverity::Info: return "error-info"; - case ErrorSeverity::Warning: return "error-warning"; - case ErrorSeverity::Error: return "error-error"; - case ErrorSeverity::Critical: return "error-critical"; - case ErrorSeverity::Fatal: return "error-fatal"; - default: return "error-error"; + case ErrorSeverity::Trace: + return "error-trace"; + case ErrorSeverity::Debug: + return "error-debug"; + case ErrorSeverity::Info: + return "error-info"; + case ErrorSeverity::Warning: + return "error-warning"; + case ErrorSeverity::Error: + return "error-error"; + case ErrorSeverity::Critical: + return "error-critical"; + case ErrorSeverity::Fatal: + return "error-fatal"; + default: + return "error-error"; } } // StructuredFormatter implementation StructuredFormatter::StructuredFormatter() - : fieldSeparator_(" ") - , keyValueSeparator_("=") { + : fieldSeparator_(" "), keyValueSeparator_("=") { options_["field_separator"] = " "; options_["key_value_separator"] = "="; - fieldOrder_ = {"timestamp", "severity", "category", "code", "message", "correlation_id", "error_id"}; + fieldOrder_ = {"timestamp", "severity", "category", "code", + "message", "correlation_id", "error_id"}; } std::string StructuredFormatter::format(std::shared_ptr context) { - if (!context) return ""; + if (!context) + return ""; std::stringstream ss; for (size_t i = 0; i < fieldOrder_.size(); ++i) { - if (i > 0) ss << fieldSeparator_; + if (i > 0) + ss << fieldSeparator_; const std::string& field = fieldOrder_[i]; if (field == "timestamp") { - auto time_t = std::chrono::system_clock::to_time_t(context->getTimestamp()); + auto time_t = + std::chrono::system_clock::to_time_t(context->getTimestamp()); ss << formatField("timestamp", std::to_string(time_t)); } else if (field == "severity") { - ss << formatField("severity", std::string(severityToString(context->getSeverity()))); + ss << formatField( + "severity", + std::string(severityToString(context->getSeverity()))); } else if (field == "category") { - ss << formatField("category", std::string(categoryToString(context->getCategory()))); + ss << formatField( + "category", + std::string(categoryToString(context->getCategory()))); } else if (field == "code") { ss << formatField("code", std::to_string(context->getErrorCode())); } else if (field == "message") { ss << formatField("message", context->getMessage()); - } else if (field == "correlation_id" && !context->getCorrelationId().empty()) { + } else if (field == "correlation_id" && + !context->getCorrelationId().empty()) { ss << formatField("correlation_id", context->getCorrelationId()); } else if (field == "error_id") { ss << formatField("error_id", context->getErrorId()); @@ -557,7 +668,8 @@ std::string StructuredFormatter::format(std::shared_ptr context) { return ss.str(); } -void StructuredFormatter::setOption(const std::string& key, const std::string& value) { +void StructuredFormatter::setOption(const std::string& key, + const std::string& value) { options_[key] = value; if (key == "field_separator") { @@ -572,8 +684,9 @@ std::string StructuredFormatter::getOption(const std::string& key) const { return it != options_.end() ? it->second : ""; } -std::string StructuredFormatter::formatField(const std::string& key, const std::string& value) const { +std::string StructuredFormatter::formatField(const std::string& key, + const std::string& value) const { return key + keyValueSeparator_ + value; } -} // namespace atom::error +} // namespace atom::error diff --git a/atom/error/error_formatter.hpp b/atom/error/error_formatter.hpp index 82c97286..b2843aab 100644 --- a/atom/error/error_formatter.hpp +++ b/atom/error/error_formatter.hpp @@ -8,20 +8,21 @@ Date: 2024-12-22 -Description: Comprehensive error formatting system with customizable output formats +Description: Comprehensive error formatting system with customizable output +formats **************************************************/ #ifndef ATOM_ERROR_FORMATTER_HPP #define ATOM_ERROR_FORMATTER_HPP +#include +#include #include +#include #include #include #include -#include -#include -#include #include "error_context.hpp" @@ -69,22 +70,24 @@ enum class Color { class ErrorFormatter { public: virtual ~ErrorFormatter() = default; - + /** * @brief Format error context to string */ virtual std::string format(std::shared_ptr context) = 0; - + /** * @brief Format multiple error contexts */ - virtual std::string formatMultiple(const std::vector>& contexts); - + virtual std::string formatMultiple( + const std::vector>& contexts); + /** * @brief Set formatting options */ - virtual void setOption(const std::string& key, const std::string& value) = 0; - + virtual void setOption(const std::string& key, + const std::string& value) = 0; + /** * @brief Get formatting options */ @@ -97,7 +100,7 @@ class ErrorFormatter { class PlainTextFormatter : public ErrorFormatter { public: PlainTextFormatter(); - + std::string format(std::shared_ptr context) override; void setOption(const std::string& key, const std::string& value) override; std::string getOption(const std::string& key) const override; @@ -116,9 +119,10 @@ class PlainTextFormatter : public ErrorFormatter { class JsonFormatter : public ErrorFormatter { public: JsonFormatter(); - + std::string format(std::shared_ptr context) override; - std::string formatMultiple(const std::vector>& contexts) override; + std::string formatMultiple( + const std::vector>& contexts) override; void setOption(const std::string& key, const std::string& value) override; std::string getOption(const std::string& key) const override; @@ -126,9 +130,11 @@ class JsonFormatter : public ErrorFormatter { std::unordered_map options_; bool prettyPrint_; int indentSize_; - + std::string escapeJsonString(const std::string& str) const; - std::string formatJsonValue(const std::string& key, const std::string& value, bool isLast = false) const; + std::string formatJsonValue(const std::string& key, + const std::string& value, + bool isLast = false) const; }; /** @@ -137,7 +143,7 @@ class JsonFormatter : public ErrorFormatter { class ColoredFormatter : public ErrorFormatter { public: ColoredFormatter(); - + std::string format(std::shared_ptr context) override; void setOption(const std::string& key, const std::string& value) override; std::string getOption(const std::string& key) const override; @@ -146,7 +152,7 @@ class ColoredFormatter : public ErrorFormatter { std::unordered_map options_; std::unordered_map severityColors_; bool enableColors_; - + std::string colorize(const std::string& text, Color color) const; Color getSeverityColor(ErrorSeverity severity) const; }; @@ -157,9 +163,10 @@ class ColoredFormatter : public ErrorFormatter { class HtmlFormatter : public ErrorFormatter { public: HtmlFormatter(); - + std::string format(std::shared_ptr context) override; - std::string formatMultiple(const std::vector>& contexts) override; + std::string formatMultiple( + const std::vector>& contexts) override; void setOption(const std::string& key, const std::string& value) override; std::string getOption(const std::string& key) const override; @@ -167,7 +174,7 @@ class HtmlFormatter : public ErrorFormatter { std::unordered_map options_; bool includeCSS_; std::string cssStyle_; - + std::string escapeHtml(const std::string& str) const; std::string getSeverityClass(ErrorSeverity severity) const; }; @@ -178,7 +185,7 @@ class HtmlFormatter : public ErrorFormatter { class StructuredFormatter : public ErrorFormatter { public: StructuredFormatter(); - + std::string format(std::shared_ptr context) override; void setOption(const std::string& key, const std::string& value) override; std::string getOption(const std::string& key) const override; @@ -188,8 +195,9 @@ class StructuredFormatter : public ErrorFormatter { std::string fieldSeparator_; std::string keyValueSeparator_; std::vector fieldOrder_; - - std::string formatField(const std::string& key, const std::string& value) const; + + std::string formatField(const std::string& key, + const std::string& value) const; }; /** @@ -198,47 +206,52 @@ class StructuredFormatter : public ErrorFormatter { class ErrorLocalizer { public: ErrorLocalizer(); - + /** * @brief Set current locale */ void setLocale(const std::string& locale); - + /** * @brief Get current locale */ std::string getCurrentLocale() const; - + /** * @brief Add translation for error code */ - void addTranslation(const std::string& locale, int errorCode, const std::string& message); - + void addTranslation(const std::string& locale, int errorCode, + const std::string& message); + /** * @brief Add translation for severity level */ - void addSeverityTranslation(const std::string& locale, ErrorSeverity severity, const std::string& translation); - + void addSeverityTranslation(const std::string& locale, + ErrorSeverity severity, + const std::string& translation); + /** * @brief Add translation for category */ - void addCategoryTranslation(const std::string& locale, ErrorCategory category, const std::string& translation); - + void addCategoryTranslation(const std::string& locale, + ErrorCategory category, + const std::string& translation); + /** * @brief Get localized error message */ std::string getLocalizedMessage(int errorCode) const; - + /** * @brief Get localized severity string */ std::string getLocalizedSeverity(ErrorSeverity severity) const; - + /** * @brief Get localized category string */ std::string getLocalizedCategory(ErrorCategory category) const; - + /** * @brief Load translations from file */ @@ -246,9 +259,14 @@ class ErrorLocalizer { private: std::string currentLocale_; - std::unordered_map> errorTranslations_; - std::unordered_map> severityTranslations_; - std::unordered_map> categoryTranslations_; + std::unordered_map> + errorTranslations_; + std::unordered_map> + severityTranslations_; + std::unordered_map> + categoryTranslations_; }; /** @@ -260,24 +278,29 @@ class ErrorFormatterFactory { * @brief Create formatter by output format */ static std::unique_ptr createFormatter(OutputFormat format); - + /** * @brief Register custom formatter */ - static void registerFormatter(const std::string& name, std::function()> factory); - + static void registerFormatter( + const std::string& name, + std::function()> factory); + /** * @brief Create custom formatter by name */ - static std::unique_ptr createCustomFormatter(const std::string& name); - + static std::unique_ptr createCustomFormatter( + const std::string& name); + /** * @brief Get available formatter names */ static std::vector getAvailableFormatters(); private: - static std::unordered_map()>> customFormatters_; + static std::unordered_map()>> + customFormatters_; }; /** @@ -286,49 +309,54 @@ class ErrorFormatterFactory { class ErrorDisplayManager { public: ErrorDisplayManager(); - + /** * @brief Set default formatter */ void setDefaultFormatter(std::unique_ptr formatter); - + /** * @brief Add formatter for specific output format */ - void addFormatter(OutputFormat format, std::unique_ptr formatter); - + void addFormatter(OutputFormat format, + std::unique_ptr formatter); + /** * @brief Display error using specified format */ - void displayError(std::shared_ptr context, OutputFormat format = OutputFormat::Plain); - + void displayError(std::shared_ptr context, + OutputFormat format = OutputFormat::Plain); + /** * @brief Display multiple errors */ - void displayErrors(const std::vector>& contexts, OutputFormat format = OutputFormat::Plain); - + void displayErrors( + const std::vector>& contexts, + OutputFormat format = OutputFormat::Plain); + /** * @brief Set output stream */ void setOutputStream(std::ostream& stream); - + /** * @brief Set error localizer */ void setLocalizer(std::shared_ptr localizer); - + /** * @brief Enable/disable automatic display */ void setAutoDisplay(bool enabled); private: - std::unordered_map> formatters_; + std::unordered_map> + formatters_; std::unique_ptr defaultFormatter_; std::ostream* outputStream_; std::shared_ptr localizer_; bool autoDisplay_; - + ErrorFormatter* getFormatter(OutputFormat format); }; @@ -338,11 +366,11 @@ class ErrorDisplayManager { class TemplateFormatter : public ErrorFormatter { public: explicit TemplateFormatter(const std::string& templateStr); - + std::string format(std::shared_ptr context) override; void setOption(const std::string& key, const std::string& value) override; std::string getOption(const std::string& key) const override; - + /** * @brief Set template string */ @@ -351,23 +379,31 @@ class TemplateFormatter : public ErrorFormatter { private: std::string templateStr_; std::unordered_map options_; - + std::string processTemplate(std::shared_ptr context) const; - std::string replaceVariables(const std::string& str, const std::unordered_map& variables) const; + std::string replaceVariables( + const std::string& str, + const std::unordered_map& variables) const; }; /** * @brief Convenience macros for error formatting */ -#define FORMAT_ERROR_PLAIN(context) \ - atom::error::ErrorFormatterFactory::createFormatter(atom::error::OutputFormat::Plain)->format(context) +#define FORMAT_ERROR_PLAIN(context) \ + atom::error::ErrorFormatterFactory::createFormatter( \ + atom::error::OutputFormat::Plain) \ + ->format(context) -#define FORMAT_ERROR_JSON(context) \ - atom::error::ErrorFormatterFactory::createFormatter(atom::error::OutputFormat::Json)->format(context) +#define FORMAT_ERROR_JSON(context) \ + atom::error::ErrorFormatterFactory::createFormatter( \ + atom::error::OutputFormat::Json) \ + ->format(context) -#define FORMAT_ERROR_COLORED(context) \ - atom::error::ErrorFormatterFactory::createFormatter(atom::error::OutputFormat::Colored)->format(context) +#define FORMAT_ERROR_COLORED(context) \ + atom::error::ErrorFormatterFactory::createFormatter( \ + atom::error::OutputFormat::Colored) \ + ->format(context) -} // namespace atom::error +} // namespace atom::error -#endif // ATOM_ERROR_FORMATTER_HPP +#endif // ATOM_ERROR_FORMATTER_HPP diff --git a/atom/error/error_handler.cpp b/atom/error/error_handler.cpp index bd2221af..178ebd6b 100644 --- a/atom/error/error_handler.cpp +++ b/atom/error/error_handler.cpp @@ -14,37 +14,34 @@ Description: Implementation of thread-safe error handling system #include "error_handler.hpp" #include +#include #include #include -#include namespace atom::error { // ErrorReporter implementation ErrorReporter::ErrorReporter() - : running_(false) - , maxQueueSize_(10000) - , aggregationStrategy_(AggregationStrategy::None) - , aggregationWindow_(std::chrono::milliseconds(1000)) - , totalErrors_(0) - , processedErrors_(0) - , filteredErrors_(0) - , droppedErrors_(0) { - + : running_(false), + maxQueueSize_(10000), + aggregationStrategy_(AggregationStrategy::None), + aggregationWindow_(std::chrono::milliseconds(1000)), + totalErrors_(0), + processedErrors_(0), + filteredErrors_(0), + droppedErrors_(0) { // Initialize severity stats for (int i = 0; i <= static_cast(ErrorSeverity::Fatal); ++i) { severityStats_[static_cast(i)] = 0; } - + // Initialize category stats for (int i = 0; i <= static_cast(ErrorCategory::External); ++i) { categoryStats_[static_cast(i)] = 0; } } -ErrorReporter::~ErrorReporter() { - stop(); -} +ErrorReporter::~ErrorReporter() { stop(); } void ErrorReporter::start() { std::lock_guard lock(queueMutex_); @@ -57,36 +54,36 @@ void ErrorReporter::start() { void ErrorReporter::stop() { running_ = false; queueCondition_.notify_all(); - + if (processingThread_.joinable()) { processingThread_.join(); } } -auto ErrorReporter::isRunning() const -> bool { - return running_.load(); -} +auto ErrorReporter::isRunning() const -> bool { return running_.load(); } void ErrorReporter::reportError(std::shared_ptr context) { - if (!context) return; - + if (!context) + return; + totalErrors_++; - + std::unique_lock lock(queueMutex_); - + // Check queue size limit if (errorQueue_.size() >= maxQueueSize_) { droppedErrors_++; return; } - + errorQueue_.push(context); lock.unlock(); - + queueCondition_.notify_one(); } -void ErrorReporter::addHandler(const std::string& name, ErrorHandlerCallback handler) { +void ErrorReporter::addHandler(const std::string& name, + ErrorHandlerCallback handler) { std::unique_lock lock(handlersMutex_); handlers_[name] = std::move(handler); } @@ -116,25 +113,26 @@ void ErrorReporter::setAggregationWindow(std::chrono::milliseconds window) { aggregationWindow_ = window; } -auto ErrorReporter::getStatistics() const -> std::unordered_map { +auto ErrorReporter::getStatistics() const + -> std::unordered_map { std::unordered_map stats; - + stats["total_errors"] = totalErrors_.load(); stats["processed_errors"] = processedErrors_.load(); stats["filtered_errors"] = filteredErrors_.load(); stats["dropped_errors"] = droppedErrors_.load(); stats["queue_size"] = static_cast(getQueueSize()); - + // Severity statistics for (const auto& [severity, count] : severityStats_) { stats[std::string(severityToString(severity))] = count.load(); } - + // Category statistics for (const auto& [category, count] : categoryStats_) { stats[std::string(categoryToString(category))] = count.load(); } - + return stats; } @@ -143,11 +141,11 @@ void ErrorReporter::clearStatistics() { processedErrors_ = 0; filteredErrors_ = 0; droppedErrors_ = 0; - + for (auto& [severity, count] : severityStats_) { count = 0; } - + for (auto& [category, count] : categoryStats_) { count = 0; } @@ -166,40 +164,40 @@ auto ErrorReporter::getQueueSize() const -> size_t { void ErrorReporter::processingLoop() { while (running_.load()) { std::unique_lock lock(queueMutex_); - - queueCondition_.wait(lock, [this] { - return !errorQueue_.empty() || !running_.load(); - }); - + + queueCondition_.wait( + lock, [this] { return !errorQueue_.empty() || !running_.load(); }); + while (!errorQueue_.empty()) { auto context = errorQueue_.front(); errorQueue_.pop(); lock.unlock(); - + processError(context); - + lock.lock(); } } } void ErrorReporter::processError(std::shared_ptr context) { - if (!context) return; - + if (!context) + return; + // Apply filters if (!shouldProcess(context)) { filteredErrors_++; return; } - + // Update statistics updateStatistics(context); - + // Handle aggregation if (aggregationStrategy_ != AggregationStrategy::None) { aggregateError(context); } - + // Call handlers std::shared_lock lock(handlersMutex_); for (const auto& [name, handler] : handlers_) { @@ -207,16 +205,17 @@ void ErrorReporter::processError(std::shared_ptr context) { handler(context); } catch (const std::exception& e) { // Log handler error but continue processing - std::cerr << "Error in handler '" << name << "': " << e.what() << std::endl; + std::cerr << "Error in handler '" << name << "': " << e.what() + << std::endl; } } - + processedErrors_++; } bool ErrorReporter::shouldProcess(std::shared_ptr context) { std::shared_lock lock(handlersMutex_); - + for (const auto& [name, filter] : filters_) { try { if (!filter(context)) { @@ -224,10 +223,11 @@ bool ErrorReporter::shouldProcess(std::shared_ptr context) { } } catch (const std::exception& e) { // Log filter error but continue processing - std::cerr << "Error in filter '" << name << "': " << e.what() << std::endl; + std::cerr << "Error in filter '" << name << "': " << e.what() + << std::endl; } } - + return true; } @@ -238,10 +238,10 @@ void ErrorReporter::updateStatistics(std::shared_ptr context) { void ErrorReporter::aggregateError(std::shared_ptr context) { std::lock_guard lock(aggregationMutex_); - + std::string key = getAggregationKey(context); auto now = std::chrono::steady_clock::now(); - + // Check if we need to flush old aggregated errors auto it = aggregationTimestamps_.find(key); if (it != aggregationTimestamps_.end()) { @@ -260,11 +260,12 @@ void ErrorReporter::aggregateError(std::shared_ptr context) { } else { aggregationTimestamps_[key] = now; } - + aggregatedErrors_[key].push_back(context); } -std::string ErrorReporter::getAggregationKey(std::shared_ptr context) { +std::string ErrorReporter::getAggregationKey( + std::shared_ptr context) { switch (aggregationStrategy_) { case AggregationStrategy::BySeverity: return std::string(severityToString(context->getSeverity())); @@ -274,14 +275,16 @@ std::string ErrorReporter::getAggregationKey(std::shared_ptr conte return std::to_string(context->getErrorCode()); case AggregationStrategy::ByCorrelation: return context->getCorrelationId(); - case AggregationStrategy::ByTimeWindow: - { - auto timestamp = context->getTimestamp(); - auto time_t = std::chrono::system_clock::to_time_t(timestamp); - auto window_seconds = std::chrono::duration_cast(aggregationWindow_).count(); - auto window_start = (time_t / window_seconds) * window_seconds; - return std::to_string(window_start); - } + case AggregationStrategy::ByTimeWindow: { + auto timestamp = context->getTimestamp(); + auto time_t = std::chrono::system_clock::to_time_t(timestamp); + auto window_seconds = + std::chrono::duration_cast( + aggregationWindow_) + .count(); + auto window_start = (time_t / window_seconds) * window_seconds; + return std::to_string(window_start); + } default: return context->getErrorId(); } @@ -291,7 +294,8 @@ std::string ErrorReporter::getAggregationKey(std::shared_ptr conte ErrorAggregator::ErrorAggregator() = default; void ErrorAggregator::addError(std::shared_ptr context) { - if (!context) return; + if (!context) + return; std::unique_lock lock(aggregationMutex_); std::string key = context->getCorrelationId(); @@ -306,7 +310,9 @@ auto ErrorAggregator::getAggregatedErrors(const std::string& key) const -> std::vector> { std::shared_lock lock(aggregationMutex_); auto it = aggregatedErrors_.find(key); - return it != aggregatedErrors_.end() ? it->second : std::vector>{}; + return it != aggregatedErrors_.end() + ? it->second + : std::vector>{}; } auto ErrorAggregator::getAggregationKeys() const -> std::vector { @@ -333,12 +339,12 @@ void ErrorAggregator::clearOlderThan(std::chrono::minutes maxAge) { for (auto it = aggregatedErrors_.begin(); it != aggregatedErrors_.end();) { auto& errors = it->second; errors.erase( - std::remove_if(errors.begin(), errors.end(), + std::remove_if( + errors.begin(), errors.end(), [cutoff](const std::shared_ptr& context) { return context && context->getTimestamp() < cutoff; }), - errors.end() - ); + errors.end()); if (errors.empty()) { it = aggregatedErrors_.erase(it); @@ -348,7 +354,8 @@ void ErrorAggregator::clearOlderThan(std::chrono::minutes maxAge) { } } -auto ErrorAggregator::getStatistics() const -> std::unordered_map { +auto ErrorAggregator::getStatistics() const + -> std::unordered_map { std::shared_lock lock(aggregationMutex_); std::unordered_map stats; @@ -376,9 +383,10 @@ void GlobalErrorHandler::initialize() { aggregator_ = std::make_unique(); // Add default handler that forwards to aggregator - reporter_->addHandler("aggregator", [this](std::shared_ptr context) { - aggregator_->addError(context); - }); + reporter_->addHandler("aggregator", + [this](std::shared_ptr context) { + aggregator_->addError(context); + }); reporter_->start(); initialized_ = true; @@ -442,10 +450,9 @@ void GlobalErrorHandler::setUnhandledExceptionHandler() { void GlobalErrorHandler::handleUnhandledException() { try { - auto context = ErrorContext::create( - static_cast(ErrorCodeBase::Failed), - "Unhandled exception occurred" - ); + auto context = + ErrorContext::create(static_cast(ErrorCodeBase::Failed), + "Unhandled exception occurred"); context->addTag("unhandled_exception"); context->setSystemInfo("severity", "fatal"); @@ -460,9 +467,7 @@ void GlobalErrorHandler::handleUnhandledException() { thread_local std::unique_ptr tlsErrorHandler; ThreadLocalErrorHandler::ThreadLocalErrorHandler() - : errorCount_(0) - , startTime_(std::chrono::steady_clock::now()) { -} + : errorCount_(0), startTime_(std::chrono::steady_clock::now()) {} ThreadLocalErrorHandler::~ThreadLocalErrorHandler() = default; @@ -470,14 +475,16 @@ void ThreadLocalErrorHandler::setHandler(ErrorHandlerCallback handler) { handler_ = std::move(handler); } -void ThreadLocalErrorHandler::reportError(std::shared_ptr context) { +void ThreadLocalErrorHandler::reportError( + std::shared_ptr context) { errorCount_++; if (handler_) { try { handler_(context); } catch (const std::exception& e) { - std::cerr << "Error in thread-local handler: " << e.what() << std::endl; + std::cerr << "Error in thread-local handler: " << e.what() + << std::endl; } } @@ -485,15 +492,17 @@ void ThreadLocalErrorHandler::reportError(std::shared_ptr context) GlobalErrorHandler::getInstance().reportError(context); } -auto ThreadLocalErrorHandler::getStatistics() const -> std::unordered_map { +auto ThreadLocalErrorHandler::getStatistics() const + -> std::unordered_map { std::unordered_map stats; stats["thread_error_count"] = errorCount_.load(); auto duration = std::chrono::steady_clock::now() - startTime_; - auto seconds = std::chrono::duration_cast(duration).count(); + auto seconds = + std::chrono::duration_cast(duration).count(); stats["thread_uptime_seconds"] = static_cast(seconds); return stats; } -} // namespace atom::error +} // namespace atom::error diff --git a/atom/error/error_handler.hpp b/atom/error/error_handler.hpp index 8316ae3c..525b4ed8 100644 --- a/atom/error/error_handler.hpp +++ b/atom/error/error_handler.hpp @@ -26,8 +26,8 @@ Description: Thread-safe error handling system with aggregation and reporting #include #include -#include "error_context.hpp" #include "error_code.hpp" +#include "error_context.hpp" namespace atom::error { @@ -62,77 +62,78 @@ class ErrorReporter { * @brief Constructor */ ErrorReporter(); - + /** * @brief Destructor */ ~ErrorReporter(); - + /** * @brief Start the error reporter */ void start(); - + /** * @brief Stop the error reporter */ void stop(); - + /** * @brief Check if the reporter is running */ [[nodiscard]] auto isRunning() const -> bool; - + /** * @brief Report an error */ void reportError(std::shared_ptr context); - + /** * @brief Add error handler callback */ void addHandler(const std::string& name, ErrorHandlerCallback handler); - + /** * @brief Remove error handler callback */ void removeHandler(const std::string& name); - + /** * @brief Add error filter */ void addFilter(const std::string& name, ErrorFilter filter); - + /** * @brief Remove error filter */ void removeFilter(const std::string& name); - + /** * @brief Set aggregation strategy */ void setAggregationStrategy(AggregationStrategy strategy); - + /** * @brief Set aggregation time window (for time-based aggregation) */ void setAggregationWindow(std::chrono::milliseconds window); - + /** * @brief Get error statistics */ - [[nodiscard]] auto getStatistics() const -> std::unordered_map; - + [[nodiscard]] auto getStatistics() const + -> std::unordered_map; + /** * @brief Clear error statistics */ void clearStatistics(); - + /** * @brief Set maximum queue size */ void setMaxQueueSize(size_t maxSize); - + /** * @brief Get current queue size */ @@ -145,29 +146,31 @@ class ErrorReporter { void updateStatistics(std::shared_ptr context); void aggregateError(std::shared_ptr context); std::string getAggregationKey(std::shared_ptr context); - + // Threading std::atomic running_; std::thread processingThread_; - + // Error queue std::queue> errorQueue_; mutable std::mutex queueMutex_; std::condition_variable queueCondition_; size_t maxQueueSize_; - + // Handlers and filters std::unordered_map handlers_; std::unordered_map filters_; mutable std::shared_mutex handlersMutex_; - + // Aggregation AggregationStrategy aggregationStrategy_; std::chrono::milliseconds aggregationWindow_; - std::unordered_map>> aggregatedErrors_; - std::unordered_map aggregationTimestamps_; + std::unordered_map>> + aggregatedErrors_; + std::unordered_map + aggregationTimestamps_; mutable std::mutex aggregationMutex_; - + // Statistics std::atomic totalErrors_; std::atomic processedErrors_; @@ -187,45 +190,47 @@ class ErrorAggregator { * @brief Constructor */ ErrorAggregator(); - + /** * @brief Destructor */ ~ErrorAggregator() = default; - + /** * @brief Add error to aggregation */ void addError(std::shared_ptr context); - + /** * @brief Get aggregated errors by key */ - [[nodiscard]] auto getAggregatedErrors(const std::string& key) const + [[nodiscard]] auto getAggregatedErrors(const std::string& key) const -> std::vector>; - + /** * @brief Get all aggregation keys */ [[nodiscard]] auto getAggregationKeys() const -> std::vector; - + /** * @brief Clear aggregated errors */ void clear(); - + /** * @brief Clear aggregated errors older than specified time */ void clearOlderThan(std::chrono::minutes maxAge); - + /** * @brief Get aggregation statistics */ - [[nodiscard]] auto getStatistics() const -> std::unordered_map; + [[nodiscard]] auto getStatistics() const + -> std::unordered_map; private: - std::unordered_map>> aggregatedErrors_; + std::unordered_map>> + aggregatedErrors_; mutable std::shared_mutex aggregationMutex_; }; @@ -238,37 +243,37 @@ class GlobalErrorHandler { * @brief Get the singleton instance */ static auto getInstance() -> GlobalErrorHandler&; - + /** * @brief Initialize the global error handler */ void initialize(); - + /** * @brief Shutdown the global error handler */ void shutdown(); - + /** * @brief Report an error globally */ void reportError(std::shared_ptr context); - + /** * @brief Get the error reporter */ [[nodiscard]] auto getReporter() -> ErrorReporter&; - + /** * @brief Get the error aggregator */ [[nodiscard]] auto getAggregator() -> ErrorAggregator&; - + /** * @brief Set global error handler callback */ void setGlobalHandler(ErrorHandlerCallback handler); - + /** * @brief Set unhandled exception handler */ @@ -279,9 +284,9 @@ class GlobalErrorHandler { ~GlobalErrorHandler() = default; GlobalErrorHandler(const GlobalErrorHandler&) = delete; GlobalErrorHandler& operator=(const GlobalErrorHandler&) = delete; - + void handleUnhandledException(); - + std::unique_ptr reporter_; std::unique_ptr aggregator_; ErrorHandlerCallback globalHandler_; @@ -298,26 +303,27 @@ class ThreadLocalErrorHandler { * @brief Constructor */ ThreadLocalErrorHandler(); - + /** * @brief Destructor */ ~ThreadLocalErrorHandler(); - + /** * @brief Set thread-local error handler */ void setHandler(ErrorHandlerCallback handler); - + /** * @brief Report error in current thread */ void reportError(std::shared_ptr context); - + /** * @brief Get thread-local error statistics */ - [[nodiscard]] auto getStatistics() const -> std::unordered_map; + [[nodiscard]] auto getStatistics() const + -> std::unordered_map; private: ErrorHandlerCallback handler_; @@ -328,21 +334,22 @@ class ThreadLocalErrorHandler { /** * @brief Macros for convenient error reporting */ -#define REPORT_ERROR(errorCode, message) \ - do { \ - auto context = CREATE_ERROR_CONTEXT(errorCode, message); \ +#define REPORT_ERROR(errorCode, message) \ + do { \ + auto context = CREATE_ERROR_CONTEXT(errorCode, message); \ atom::error::GlobalErrorHandler::getInstance().reportError(context); \ - } while(0) - -#define REPORT_ERROR_WITH_CORRELATION(errorCode, correlationId, message) \ - do { \ - auto context = atom::error::ErrorContext::createWithCorrelation(errorCode, correlationId, message); \ - context->setSystemInfo("file", ATOM_FILE_NAME) \ - ->setSystemInfo("line", std::to_string(ATOM_FILE_LINE)) \ - ->setSystemInfo("function", ATOM_FUNC_NAME); \ + } while (0) + +#define REPORT_ERROR_WITH_CORRELATION(errorCode, correlationId, message) \ + do { \ + auto context = atom::error::ErrorContext::createWithCorrelation( \ + errorCode, correlationId, message); \ + context->setSystemInfo("file", ATOM_FILE_NAME) \ + ->setSystemInfo("line", std::to_string(ATOM_FILE_LINE)) \ + ->setSystemInfo("function", ATOM_FUNC_NAME); \ atom::error::GlobalErrorHandler::getInstance().reportError(context); \ - } while(0) + } while (0) -} // namespace atom::error +} // namespace atom::error -#endif // ATOM_ERROR_HANDLER_HPP +#endif // ATOM_ERROR_HANDLER_HPP diff --git a/atom/error/error_recovery.cpp b/atom/error/error_recovery.cpp index 826f62b1..1de27332 100644 --- a/atom/error/error_recovery.cpp +++ b/atom/error/error_recovery.cpp @@ -14,103 +14,122 @@ Description: Implementation of error recovery framework #include "error_recovery.hpp" #include -#include #include -#include #include +#include +#include #if __cplusplus >= 202002L #include #else // Fallback for pre-C++20 compilers -#include #include +#include #endif namespace atom::error { // FixedIntervalRetryPolicy implementation -FixedIntervalRetryPolicy::FixedIntervalRetryPolicy(int maxRetries, std::chrono::milliseconds interval) +FixedIntervalRetryPolicy::FixedIntervalRetryPolicy( + int maxRetries, std::chrono::milliseconds interval) : maxRetries_(maxRetries), interval_(interval), currentAttempt_(0) {} -bool FixedIntervalRetryPolicy::shouldRetry(std::shared_ptr context) { - if (!context) return false; +bool FixedIntervalRetryPolicy::shouldRetry( + std::shared_ptr context) { + if (!context) + return false; return currentAttempt_ < maxRetries_ && context->canRetry(); } -std::chrono::milliseconds FixedIntervalRetryPolicy::getRetryDelay([[maybe_unused]] int attemptNumber) { +std::chrono::milliseconds FixedIntervalRetryPolicy::getRetryDelay( + [[maybe_unused]] int attemptNumber) { return interval_; } -void FixedIntervalRetryPolicy::reset() { - currentAttempt_ = 0; -} +void FixedIntervalRetryPolicy::reset() { currentAttempt_ = 0; } std::unique_ptr FixedIntervalRetryPolicy::clone() const { return std::make_unique(maxRetries_, interval_); } // ExponentialBackoffRetryPolicy implementation -ExponentialBackoffRetryPolicy::ExponentialBackoffRetryPolicy(int maxRetries, std::chrono::milliseconds baseDelay, - double multiplier, std::chrono::milliseconds maxDelay) - : maxRetries_(maxRetries), baseDelay_(baseDelay), multiplier_(multiplier), maxDelay_(maxDelay), currentAttempt_(0) {} - -bool ExponentialBackoffRetryPolicy::shouldRetry(std::shared_ptr context) { - if (!context) return false; +ExponentialBackoffRetryPolicy::ExponentialBackoffRetryPolicy( + int maxRetries, std::chrono::milliseconds baseDelay, double multiplier, + std::chrono::milliseconds maxDelay) + : maxRetries_(maxRetries), + baseDelay_(baseDelay), + multiplier_(multiplier), + maxDelay_(maxDelay), + currentAttempt_(0) {} + +bool ExponentialBackoffRetryPolicy::shouldRetry( + std::shared_ptr context) { + if (!context) + return false; return currentAttempt_ < maxRetries_ && context->canRetry(); } -std::chrono::milliseconds ExponentialBackoffRetryPolicy::getRetryDelay(int attemptNumber) { - auto delay = baseDelay_ * static_cast(std::pow(multiplier_, attemptNumber)); +std::chrono::milliseconds ExponentialBackoffRetryPolicy::getRetryDelay( + int attemptNumber) { + auto delay = baseDelay_ * + static_cast(std::pow(multiplier_, attemptNumber)); return std::min(delay, maxDelay_); } -void ExponentialBackoffRetryPolicy::reset() { - currentAttempt_ = 0; -} +void ExponentialBackoffRetryPolicy::reset() { currentAttempt_ = 0; } std::unique_ptr ExponentialBackoffRetryPolicy::clone() const { - return std::make_unique(maxRetries_, baseDelay_, multiplier_, maxDelay_); + return std::make_unique( + maxRetries_, baseDelay_, multiplier_, maxDelay_); } // JitteredRetryPolicy implementation -JitteredRetryPolicy::JitteredRetryPolicy(std::unique_ptr basePolicy, double jitterFactor) - : basePolicy_(std::move(basePolicy)), jitterFactor_(jitterFactor), rng_(std::random_device{}()), distribution_(-jitterFactor, jitterFactor) {} +JitteredRetryPolicy::JitteredRetryPolicy( + std::unique_ptr basePolicy, double jitterFactor) + : basePolicy_(std::move(basePolicy)), + jitterFactor_(jitterFactor), + rng_(std::random_device{}()), + distribution_(-jitterFactor, jitterFactor) {} bool JitteredRetryPolicy::shouldRetry(std::shared_ptr context) { return basePolicy_->shouldRetry(context); } -std::chrono::milliseconds JitteredRetryPolicy::getRetryDelay(int attemptNumber) { +std::chrono::milliseconds JitteredRetryPolicy::getRetryDelay( + int attemptNumber) { auto baseDelay = basePolicy_->getRetryDelay(attemptNumber); double jitter = distribution_(rng_); - auto jitteredDelay = baseDelay + std::chrono::milliseconds(static_cast(baseDelay.count() * jitter)); + auto jitteredDelay = + baseDelay + std::chrono::milliseconds( + static_cast(baseDelay.count() * jitter)); return std::max(jitteredDelay, std::chrono::milliseconds(0)); } -void JitteredRetryPolicy::reset() { - basePolicy_->reset(); -} +void JitteredRetryPolicy::reset() { basePolicy_->reset(); } std::unique_ptr JitteredRetryPolicy::clone() const { - return std::make_unique(basePolicy_->clone(), jitterFactor_); + return std::make_unique(basePolicy_->clone(), + jitterFactor_); } // CircuitBreaker implementation -CircuitBreaker::CircuitBreaker(int failureThreshold, std::chrono::milliseconds timeout, int successThreshold) - : state_(CircuitBreakerState::Closed) - , failureCount_(0) - , successCount_(0) - , failureThreshold_(failureThreshold) - , successThreshold_(successThreshold) - , timeout_(timeout) - , totalFailures_(0) - , totalSuccesses_(0) {} - -template -auto CircuitBreaker::execute(Func&& func, Args&&... args) -> decltype(func(args...)) { +CircuitBreaker::CircuitBreaker(int failureThreshold, + std::chrono::milliseconds timeout, + int successThreshold) + : state_(CircuitBreakerState::Closed), + failureCount_(0), + successCount_(0), + failureThreshold_(failureThreshold), + successThreshold_(successThreshold), + timeout_(timeout), + totalFailures_(0), + totalSuccesses_(0) {} + +template +auto CircuitBreaker::execute(Func&& func, + Args&&... args) -> decltype(func(args...)) { std::lock_guard lock(mutex_); - + if (state_ == CircuitBreakerState::Open) { if (shouldAttemptReset()) { transitionToHalfOpen(); @@ -118,7 +137,7 @@ auto CircuitBreaker::execute(Func&& func, Args&&... args) -> decltype(func(args. throw std::runtime_error("Circuit breaker is open"); } } - + try { auto result = func(args...); recordSuccess(); @@ -132,14 +151,14 @@ auto CircuitBreaker::execute(Func&& func, Args&&... args) -> decltype(func(args. void CircuitBreaker::recordSuccess() { std::lock_guard lock(mutex_); totalSuccesses_++; - + if (state_ == CircuitBreakerState::HalfOpen) { successCount_++; if (successCount_ >= successThreshold_) { transitionToClosed(); } } else if (state_ == CircuitBreakerState::Closed) { - failureCount_ = 0; // Reset failure count on success + failureCount_ = 0; // Reset failure count on success } } @@ -147,7 +166,7 @@ void CircuitBreaker::recordFailure() { std::lock_guard lock(mutex_); totalFailures_++; lastFailureTime_ = std::chrono::steady_clock::now(); - + if (state_ == CircuitBreakerState::Closed) { failureCount_++; if (failureCount_ >= failureThreshold_) { @@ -166,7 +185,7 @@ CircuitBreakerState CircuitBreaker::getState() const { std::unordered_map CircuitBreaker::getStatistics() const { std::lock_guard lock(mutex_); std::unordered_map stats; - + stats["state"] = static_cast(state_); stats["failure_count"] = failureCount_; stats["success_count"] = successCount_; @@ -174,7 +193,7 @@ std::unordered_map CircuitBreaker::getStatistics() const { stats["total_successes"] = totalSuccesses_.load(); stats["failure_threshold"] = failureThreshold_; stats["success_threshold"] = successThreshold_; - + return stats; } @@ -208,68 +227,75 @@ bool CircuitBreaker::shouldAttemptReset() const { } // ErrorRecoveryExecutor implementation -template -ErrorRecoveryExecutor& ErrorRecoveryExecutor::withRetryPolicy(std::unique_ptr policy) { +template +ErrorRecoveryExecutor& ErrorRecoveryExecutor::withRetryPolicy( + std::unique_ptr policy) { retryPolicy_ = std::move(policy); return *this; } -template -ErrorRecoveryExecutor& ErrorRecoveryExecutor::withCircuitBreaker(std::shared_ptr circuitBreaker) { +template +ErrorRecoveryExecutor& ErrorRecoveryExecutor::withCircuitBreaker( + std::shared_ptr circuitBreaker) { circuitBreaker_ = std::move(circuitBreaker); return *this; } -template -ErrorRecoveryExecutor& ErrorRecoveryExecutor::withFallback(std::unique_ptr> fallback) { +template +ErrorRecoveryExecutor& ErrorRecoveryExecutor::withFallback( + std::unique_ptr> fallback) { fallbackStrategy_ = std::move(fallback); return *this; } -template -ErrorRecoveryExecutor& ErrorRecoveryExecutor::withTimeout(std::chrono::milliseconds timeout) { +template +ErrorRecoveryExecutor& ErrorRecoveryExecutor::withTimeout( + std::chrono::milliseconds timeout) { timeout_ = timeout; return *this; } -template -template +template +template T ErrorRecoveryExecutor::execute(Func&& func, Args&&... args) { - return executeWithRecovery(std::forward(func), std::forward(args)...); + return executeWithRecovery(std::forward(func), + std::forward(args)...); } -template -template -std::future ErrorRecoveryExecutor::executeAsync(Func&& func, Args&&... args) { - return std::async(std::launch::async, [this, func = std::forward(func), args...]() mutable { - return executeWithRecovery(std::move(func), args...); - }); +template +template +std::future ErrorRecoveryExecutor::executeAsync(Func&& func, + Args&&... args) { + return std::async( + std::launch::async, + [this, func = std::forward(func), args...]() mutable { + return executeWithRecovery(std::move(func), args...); + }); } -template -template +template +template T ErrorRecoveryExecutor::executeWithRecovery(Func&& func, Args&&... args) { std::shared_ptr lastError; int attemptNumber = 0; - + if (retryPolicy_) { retryPolicy_->reset(); } - + while (true) { try { if (circuitBreaker_) { - return circuitBreaker_->execute(std::forward(func), std::forward(args)...); + return circuitBreaker_->execute(std::forward(func), + std::forward(args)...); } else { return func(args...); } } catch (const std::exception& e) { lastError = ErrorContext::create( - static_cast(ErrorCodeBase::Failed), - e.what() - ); + static_cast(ErrorCodeBase::Failed), e.what()); lastError->incrementRetryCount(); - + // Check if we should retry if (retryPolicy_ && retryPolicy_->shouldRetry(lastError)) { auto delay = retryPolicy_->getRetryDelay(attemptNumber); @@ -277,12 +303,12 @@ T ErrorRecoveryExecutor::executeWithRecovery(Func&& func, Args&&... args) { attemptNumber++; continue; } - + // Try fallback if available if (fallbackStrategy_ && fallbackStrategy_->isAvailable()) { return fallbackStrategy_->execute(lastError); } - + // No recovery possible, rethrow throw; } @@ -294,15 +320,18 @@ Bulkhead::Bulkhead(int maxConcurrentOperations) #if __cplusplus >= 202002L : semaphore_(maxConcurrentOperations) #else - : maxConcurrent_(maxConcurrentOperations) - , currentCount_(0) + : maxConcurrent_(maxConcurrentOperations), + currentCount_(0) #endif - , activeOperations_(0) - , totalOperations_(0) - , rejectedOperations_(0) {} + , + activeOperations_(0), + totalOperations_(0), + rejectedOperations_(0) { +} -template -auto Bulkhead::execute(Func&& func, Args&&... args) -> std::future { +template +auto Bulkhead::execute(Func&& func, + Args&&... args) -> std::future { totalOperations_++; #if __cplusplus >= 202002L @@ -324,34 +353,36 @@ auto Bulkhead::execute(Func&& func, Args&&... args) -> std::future(func), args...]() mutable { - try { - auto result = func(args...); - activeOperations_--; + return std::async( + std::launch::async, + [this, func = std::forward(func), args...]() mutable { + try { + auto result = func(args...); + activeOperations_--; #if __cplusplus >= 202002L - semaphore_.release(); + semaphore_.release(); #else - { - std::lock_guard lock(mutex_); - currentCount_--; - condition_.notify_one(); - } + { + std::lock_guard lock(mutex_); + currentCount_--; + condition_.notify_one(); + } #endif - return result; - } catch (...) { - activeOperations_--; + return result; + } catch (...) { + activeOperations_--; #if __cplusplus >= 202002L - semaphore_.release(); + semaphore_.release(); #else - { - std::lock_guard lock(mutex_); - currentCount_--; - condition_.notify_one(); - } + { + std::lock_guard lock(mutex_); + currentCount_--; + condition_.notify_one(); + } #endif - throw; - } - }); + throw; + } + }); } std::unordered_map Bulkhead::getStatistics() const { @@ -363,42 +394,63 @@ std::unordered_map Bulkhead::getStatistics() const { } // RecoveryStrategyFactory implementation -std::unique_ptr RecoveryStrategyFactory::createFixedRetry(int maxRetries, std::chrono::milliseconds interval) { +std::unique_ptr RecoveryStrategyFactory::createFixedRetry( + int maxRetries, std::chrono::milliseconds interval) { return std::make_unique(maxRetries, interval); } -std::unique_ptr RecoveryStrategyFactory::createExponentialBackoff(int maxRetries, std::chrono::milliseconds baseDelay) { - return std::make_unique(maxRetries, baseDelay); +std::unique_ptr RecoveryStrategyFactory::createExponentialBackoff( + int maxRetries, std::chrono::milliseconds baseDelay) { + return std::make_unique(maxRetries, + baseDelay); } -std::unique_ptr RecoveryStrategyFactory::createJitteredRetry(std::unique_ptr basePolicy, double jitterFactor) { - return std::make_unique(std::move(basePolicy), jitterFactor); +std::unique_ptr RecoveryStrategyFactory::createJitteredRetry( + std::unique_ptr basePolicy, double jitterFactor) { + return std::make_unique(std::move(basePolicy), + jitterFactor); } -std::shared_ptr RecoveryStrategyFactory::createCircuitBreaker(int failureThreshold, std::chrono::milliseconds timeout) { +std::shared_ptr RecoveryStrategyFactory::createCircuitBreaker( + int failureThreshold, std::chrono::milliseconds timeout) { return std::make_shared(failureThreshold, timeout); } -template -std::unique_ptr> RecoveryStrategyFactory::createDefaultFallback(T defaultValue) { +template +std::unique_ptr> +RecoveryStrategyFactory::createDefaultFallback(T defaultValue) { return std::make_unique>(std::move(defaultValue)); } -template -std::unique_ptr> RecoveryStrategyFactory::createFunctionFallback(std::function)> func) { +template +std::unique_ptr> +RecoveryStrategyFactory::createFunctionFallback( + std::function)> func) { return std::make_unique>(std::move(func)); } // Explicit template instantiations for common types -template std::unique_ptr> RecoveryStrategyFactory::createDefaultFallback(int); -template std::unique_ptr> RecoveryStrategyFactory::createDefaultFallback(std::string); -template std::unique_ptr> RecoveryStrategyFactory::createDefaultFallback(bool); -template std::unique_ptr> RecoveryStrategyFactory::createDefaultFallback(double); - -template std::unique_ptr> RecoveryStrategyFactory::createFunctionFallback(std::function)>); -template std::unique_ptr> RecoveryStrategyFactory::createFunctionFallback(std::function)>); -template std::unique_ptr> RecoveryStrategyFactory::createFunctionFallback(std::function)>); -template std::unique_ptr> RecoveryStrategyFactory::createFunctionFallback(std::function)>); +template std::unique_ptr> +RecoveryStrategyFactory::createDefaultFallback(int); +template std::unique_ptr> + RecoveryStrategyFactory::createDefaultFallback(std::string); +template std::unique_ptr> +RecoveryStrategyFactory::createDefaultFallback(bool); +template std::unique_ptr> +RecoveryStrategyFactory::createDefaultFallback(double); + +template std::unique_ptr> + RecoveryStrategyFactory::createFunctionFallback( + std::function)>); +template std::unique_ptr> + RecoveryStrategyFactory::createFunctionFallback( + std::function)>); +template std::unique_ptr> + RecoveryStrategyFactory::createFunctionFallback( + std::function)>); +template std::unique_ptr> + RecoveryStrategyFactory::createFunctionFallback( + std::function)>); // Explicit template instantiations for ErrorRecoveryExecutor template class ErrorRecoveryExecutor; @@ -407,4 +459,4 @@ template class ErrorRecoveryExecutor; template class ErrorRecoveryExecutor; template class ErrorRecoveryExecutor; -} // namespace atom::error +} // namespace atom::error diff --git a/atom/error/error_recovery.hpp b/atom/error/error_recovery.hpp index dfdbab6d..92f8dfb4 100644 --- a/atom/error/error_recovery.hpp +++ b/atom/error/error_recovery.hpp @@ -8,20 +8,21 @@ Date: 2024-12-22 -Description: Error recovery framework with retry policies and fallback strategies +Description: Error recovery framework with retry policies and fallback +strategies **************************************************/ #ifndef ATOM_ERROR_RECOVERY_HPP #define ATOM_ERROR_RECOVERY_HPP +#include #include #include +#include #include #include #include -#include -#include #include "error_context.hpp" #include "error_handler.hpp" @@ -34,22 +35,22 @@ namespace atom::error { class RetryPolicy { public: virtual ~RetryPolicy() = default; - + /** * @brief Check if operation should be retried */ virtual bool shouldRetry(std::shared_ptr context) = 0; - + /** * @brief Get delay before next retry */ virtual std::chrono::milliseconds getRetryDelay(int attemptNumber) = 0; - + /** * @brief Reset policy state */ virtual void reset() = 0; - + /** * @brief Clone the policy */ @@ -61,8 +62,9 @@ class RetryPolicy { */ class FixedIntervalRetryPolicy : public RetryPolicy { public: - FixedIntervalRetryPolicy(int maxRetries, std::chrono::milliseconds interval); - + FixedIntervalRetryPolicy(int maxRetries, + std::chrono::milliseconds interval); + bool shouldRetry(std::shared_ptr context) override; std::chrono::milliseconds getRetryDelay(int attemptNumber) override; void reset() override; @@ -79,9 +81,11 @@ class FixedIntervalRetryPolicy : public RetryPolicy { */ class ExponentialBackoffRetryPolicy : public RetryPolicy { public: - ExponentialBackoffRetryPolicy(int maxRetries, std::chrono::milliseconds baseDelay, - double multiplier = 2.0, std::chrono::milliseconds maxDelay = std::chrono::minutes(5)); - + ExponentialBackoffRetryPolicy( + int maxRetries, std::chrono::milliseconds baseDelay, + double multiplier = 2.0, + std::chrono::milliseconds maxDelay = std::chrono::minutes(5)); + bool shouldRetry(std::shared_ptr context) override; std::chrono::milliseconds getRetryDelay(int attemptNumber) override; void reset() override; @@ -100,8 +104,9 @@ class ExponentialBackoffRetryPolicy : public RetryPolicy { */ class JitteredRetryPolicy : public RetryPolicy { public: - JitteredRetryPolicy(std::unique_ptr basePolicy, double jitterFactor = 0.1); - + JitteredRetryPolicy(std::unique_ptr basePolicy, + double jitterFactor = 0.1); + bool shouldRetry(std::shared_ptr context) override; std::chrono::milliseconds getRetryDelay(int attemptNumber) override; void reset() override; @@ -118,9 +123,9 @@ class JitteredRetryPolicy : public RetryPolicy { * @brief Circuit breaker states */ enum class CircuitBreakerState { - Closed, ///< Normal operation - Open, ///< Circuit is open, failing fast - HalfOpen ///< Testing if service has recovered + Closed, ///< Normal operation + Open, ///< Circuit is open, failing fast + HalfOpen ///< Testing if service has recovered }; /** @@ -128,34 +133,35 @@ enum class CircuitBreakerState { */ class CircuitBreaker { public: - CircuitBreaker(int failureThreshold, std::chrono::milliseconds timeout, int successThreshold = 1); - + CircuitBreaker(int failureThreshold, std::chrono::milliseconds timeout, + int successThreshold = 1); + /** * @brief Execute operation with circuit breaker protection */ - template + template auto execute(Func&& func, Args&&... args) -> decltype(func(args...)); - + /** * @brief Record successful operation */ void recordSuccess(); - + /** * @brief Record failed operation */ void recordFailure(); - + /** * @brief Get current state */ CircuitBreakerState getState() const; - + /** * @brief Get failure statistics */ std::unordered_map getStatistics() const; - + /** * @brief Reset circuit breaker */ @@ -166,7 +172,7 @@ class CircuitBreaker { void transitionToHalfOpen(); void transitionToClosed(); bool shouldAttemptReset() const; - + mutable std::mutex mutex_; CircuitBreakerState state_; int failureCount_; @@ -182,16 +188,16 @@ class CircuitBreaker { /** * @brief Fallback strategy interface */ -template +template class FallbackStrategy { public: virtual ~FallbackStrategy() = default; - + /** * @brief Execute fallback operation */ virtual T execute(std::shared_ptr context) = 0; - + /** * @brief Check if fallback is available */ @@ -201,18 +207,17 @@ class FallbackStrategy { /** * @brief Default value fallback strategy */ -template +template class DefaultValueFallback : public FallbackStrategy { public: - explicit DefaultValueFallback(T defaultValue) : defaultValue_(std::move(defaultValue)) {} - + explicit DefaultValueFallback(T defaultValue) + : defaultValue_(std::move(defaultValue)) {} + T execute(std::shared_ptr context) override { return defaultValue_; } - - bool isAvailable() const override { - return true; - } + + bool isAvailable() const override { return true; } private: T defaultValue_; @@ -221,16 +226,17 @@ class DefaultValueFallback : public FallbackStrategy { /** * @brief Function-based fallback strategy */ -template +template class FunctionFallback : public FallbackStrategy { public: - explicit FunctionFallback(std::function)> fallbackFunc) + explicit FunctionFallback( + std::function)> fallbackFunc) : fallbackFunc_(std::move(fallbackFunc)) {} - + T execute(std::shared_ptr context) override { return fallbackFunc_(context); } - + bool isAvailable() const override { return static_cast(fallbackFunc_); } @@ -242,47 +248,49 @@ class FunctionFallback : public FallbackStrategy { /** * @brief Error recovery executor */ -template +template class ErrorRecoveryExecutor { public: ErrorRecoveryExecutor() = default; - + /** * @brief Set retry policy */ ErrorRecoveryExecutor& withRetryPolicy(std::unique_ptr policy); - + /** * @brief Set circuit breaker */ - ErrorRecoveryExecutor& withCircuitBreaker(std::shared_ptr circuitBreaker); - + ErrorRecoveryExecutor& withCircuitBreaker( + std::shared_ptr circuitBreaker); + /** * @brief Set fallback strategy */ - ErrorRecoveryExecutor& withFallback(std::unique_ptr> fallback); - + ErrorRecoveryExecutor& withFallback( + std::unique_ptr> fallback); + /** * @brief Set timeout */ ErrorRecoveryExecutor& withTimeout(std::chrono::milliseconds timeout); - + /** * @brief Execute operation with error recovery */ - template + template T execute(Func&& func, Args&&... args); - + /** * @brief Execute operation asynchronously with error recovery */ - template + template std::future executeAsync(Func&& func, Args&&... args); private: - template + template T executeWithRecovery(Func&& func, Args&&... args); - + std::unique_ptr retryPolicy_; std::shared_ptr circuitBreaker_; std::unique_ptr> fallbackStrategy_; @@ -295,13 +303,14 @@ class ErrorRecoveryExecutor { class Bulkhead { public: Bulkhead(int maxConcurrentOperations); - + /** * @brief Execute operation with bulkhead protection */ - template - auto execute(Func&& func, Args&&... args) -> std::future; - + template + auto execute(Func&& func, + Args&&... args) -> std::future; + /** * @brief Get current statistics */ @@ -330,53 +339,66 @@ class RecoveryStrategyFactory { /** * @brief Create fixed interval retry policy */ - static std::unique_ptr createFixedRetry(int maxRetries, std::chrono::milliseconds interval); - + static std::unique_ptr createFixedRetry( + int maxRetries, std::chrono::milliseconds interval); + /** * @brief Create exponential backoff retry policy */ - static std::unique_ptr createExponentialBackoff(int maxRetries, std::chrono::milliseconds baseDelay); - + static std::unique_ptr createExponentialBackoff( + int maxRetries, std::chrono::milliseconds baseDelay); + /** * @brief Create jittered retry policy */ - static std::unique_ptr createJitteredRetry(std::unique_ptr basePolicy, double jitterFactor = 0.1); - + static std::unique_ptr createJitteredRetry( + std::unique_ptr basePolicy, double jitterFactor = 0.1); + /** * @brief Create circuit breaker */ - static std::shared_ptr createCircuitBreaker(int failureThreshold, std::chrono::milliseconds timeout); - + static std::shared_ptr createCircuitBreaker( + int failureThreshold, std::chrono::milliseconds timeout); + /** * @brief Create default value fallback */ - template - static std::unique_ptr> createDefaultFallback(T defaultValue); - + template + static std::unique_ptr> createDefaultFallback( + T defaultValue); + /** * @brief Create function-based fallback */ - template - static std::unique_ptr> createFunctionFallback(std::function)> func); + template + static std::unique_ptr> createFunctionFallback( + std::function)> func); }; /** * @brief Convenience macros for error recovery */ -#define WITH_RETRY(maxRetries, interval) \ - atom::error::ErrorRecoveryExecutor() \ - .withRetryPolicy(atom::error::RecoveryStrategyFactory::createFixedRetry(maxRetries, interval)) - -#define WITH_EXPONENTIAL_BACKOFF(maxRetries, baseDelay) \ - atom::error::ErrorRecoveryExecutor() \ - .withRetryPolicy(atom::error::RecoveryStrategyFactory::createExponentialBackoff(maxRetries, baseDelay)) - -#define WITH_CIRCUIT_BREAKER(failureThreshold, timeout) \ - .withCircuitBreaker(atom::error::RecoveryStrategyFactory::createCircuitBreaker(failureThreshold, timeout)) - -#define WITH_FALLBACK(defaultValue) \ - .withFallback(atom::error::RecoveryStrategyFactory::createDefaultFallback(defaultValue)) - -} // namespace atom::error - -#endif // ATOM_ERROR_RECOVERY_HPP +#define WITH_RETRY(maxRetries, interval) \ + atom::error::ErrorRecoveryExecutor() \ + .withRetryPolicy( \ + atom::error::RecoveryStrategyFactory::createFixedRetry(maxRetries, \ + interval)) + +#define WITH_EXPONENTIAL_BACKOFF(maxRetries, baseDelay) \ + atom::error::ErrorRecoveryExecutor() \ + .withRetryPolicy( \ + atom::error::RecoveryStrategyFactory::createExponentialBackoff( \ + maxRetries, baseDelay)) + +#define WITH_CIRCUIT_BREAKER(failureThreshold, timeout) \ + .withCircuitBreaker( \ + atom::error::RecoveryStrategyFactory::createCircuitBreaker( \ + failureThreshold, timeout)) + +#define WITH_FALLBACK(defaultValue) \ + .withFallback(atom::error::RecoveryStrategyFactory::createDefaultFallback( \ + defaultValue)) + +} // namespace atom::error + +#endif // ATOM_ERROR_RECOVERY_HPP diff --git a/atom/error/exception.hpp b/atom/error/exception.hpp index 8847e0e3..64585c7b 100644 --- a/atom/error/exception.hpp +++ b/atom/error/exception.hpp @@ -16,10 +16,10 @@ Description: Better Exception Library #define ATOM_ERROR_EXCEPTION_HPP #include +#include #include #include #include -#include #include "../macro.hpp" #include "stacktrace.hpp" @@ -57,7 +57,6 @@ class Exception : public std::exception { } public: - template static void rethrowNested(Args &&...args) { try { diff --git a/atom/error/stacktrace.cpp b/atom/error/stacktrace.cpp index e06f6365..585238b8 100644 --- a/atom/error/stacktrace.cpp +++ b/atom/error/stacktrace.cpp @@ -1,13 +1,13 @@ #include "stacktrace.hpp" // #include "../meta/abi.hpp" -#include -#include -#include #include +#include +#include #include #include -#include +#include +#include #ifndef _WIN32 #include #endif @@ -62,21 +62,23 @@ std::string StackTrace::preferredBackend_ = "auto"; // Thread-safe initialization namespace { - std::once_flag initFlag; - std::atomic initialized{false}; - - void ensureInitialized() { - std::call_once(initFlag, []() { -// Temporarily disable Windows stacktrace due to header conflicts -// #ifdef _WIN32 -// SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES | -// SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_EXACT_SYMBOLS); -// SymInitialize(GetCurrentProcess(), nullptr, TRUE); -// #endif - initialized = true; - }); - } -} +std::once_flag initFlag; +std::atomic initialized{false}; + +void ensureInitialized() { + std::call_once(initFlag, []() { + // Temporarily disable Windows stacktrace due to header conflicts + // #ifdef _WIN32 + // SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS | + // SYMOPT_LOAD_LINES | + // SYMOPT_FAIL_CRITICAL_ERRORS | + // SYMOPT_EXACT_SYMBOLS); + // SymInitialize(GetCurrentProcess(), nullptr, TRUE); + // #endif + initialized = true; + }); +} +} // namespace // ============================================================================ // StackFrame Implementation @@ -94,12 +96,16 @@ std::string StackFrame::toString(const StackTraceConfig& config) const { // Memory address if (config.includeAddresses && address != nullptr) { - oss << " at " << stacktrace_utils::formatAddress(reinterpret_cast(address)); + oss << " at " + << stacktrace_utils::formatAddress( + reinterpret_cast(address)); } // Module information if (config.includeModules && !module.empty()) { - std::string modName = module == config.unknownModule ? module : stacktrace_utils::getBaseName(module); + std::string modName = module == config.unknownModule + ? module + : stacktrace_utils::getBaseName(module); oss << " in " << modName; if (offset > 0) { oss << " (+" << std::hex << offset << std::dec << ")"; @@ -108,7 +114,8 @@ std::string StackFrame::toString(const StackTraceConfig& config) const { // Source information if (config.includeSourceInfo && !sourceFile.empty() && sourceLine > 0) { - oss << " (" << stacktrace_utils::getBaseName(sourceFile) << ":" << sourceLine << ")"; + oss << " (" << stacktrace_utils::getBaseName(sourceFile) << ":" + << sourceLine << ")"; } return oss.str(); @@ -133,16 +140,17 @@ std::string demangle(const std::string& mangled) { std::string result = mangled; #if defined(__GNUC__) || defined(__clang__) - #ifndef _WIN32 +#ifndef _WIN32 // Fallback to direct abi::__cxa_demangle int status = 0; - char* demangled = abi::__cxa_demangle(mangled.c_str(), nullptr, nullptr, &status); + char* demangled = + abi::__cxa_demangle(mangled.c_str(), nullptr, nullptr, &status); if (status == 0 && demangled) { result = demangled; free(demangled); return result; } - #endif +#endif #endif return mangled; @@ -151,15 +159,14 @@ std::string demangle(const std::string& mangled) { std::string prettify(const std::string& input) { std::string output = input; - static const std::vector> REPLACEMENTS = { - {"std::__1::", "std::"}, - {"std::__cxx11::", "std::"}, - {"__thiscall ", ""}, - {"__cdecl ", ""}, - {", std::allocator<[^<>]+>", ""}, - {"class ", ""}, - {"struct ", ""} - }; + static const std::vector> REPLACEMENTS = + {{"std::__1::", "std::"}, + {"std::__cxx11::", "std::"}, + {"__thiscall ", ""}, + {"__cdecl ", ""}, + {", std::allocator<[^<>]+>", ""}, + {"class ", ""}, + {"struct ", ""}}; for (const auto& [from, to] : REPLACEMENTS) { try { @@ -171,8 +178,11 @@ std::string prettify(const std::string& input) { } try { - output = std::regex_replace(output, std::regex(R"(<\s*([^<> ]+)\s*>)"), "<$1>"); - output = std::regex_replace(output, std::regex(R"(<([^<>]*)<([^<>]*)>\s*([^<>]*)>)"), "<$1<$2>$3>"); + output = std::regex_replace(output, std::regex(R"(<\s*([^<> ]+)\s*>)"), + "<$1>"); + output = std::regex_replace( + output, std::regex(R"(<([^<>]*)<([^<>]*)>\s*([^<>]*)>)"), + "<$1<$2>$3>"); output = std::regex_replace(output, std::regex(R"(\s{2,})"), " "); } catch (const std::regex_error&) { // Return partially processed output if regex fails @@ -204,10 +214,10 @@ bool containsMangledNames(const std::string& str) { // Look for common C++ mangling patterns return str.find("_Z") != std::string::npos || str.find("__Z") != std::string::npos || - str.find("?") == 0; // MSVC mangling + str.find("?") == 0; // MSVC mangling } -} // namespace stacktrace_utils +} // namespace stacktrace_utils // ============================================================================ // Backend Implementations @@ -221,7 +231,7 @@ namespace backends { class BuiltinBackend : public StackTraceBackend { public: std::vector capture(const StackTraceConfig& config) override { - (void)config; // Suppress unused parameter warning + (void)config; // Suppress unused parameter warning ensureInitialized(); std::vector frames; @@ -236,18 +246,17 @@ class BuiltinBackend : public StackTraceBackend { return frames; } - std::string getName() const override { - return "builtin"; - } + std::string getName() const override { return "builtin"; } bool isAvailable() const override { - return true; // Always available + return true; // Always available } private: // Temporarily disable Windows stacktrace due to header conflicts // #ifdef _WIN32 -// void captureWindows(std::vector& frames, const StackTraceConfig& config) { +// void captureWindows(std::vector& frames, const +// StackTraceConfig& config) { // constexpr int MAX_FRAMES = 256; // void* framePtrs[MAX_FRAMES]; // @@ -275,12 +284,13 @@ class BuiltinBackend : public StackTraceBackend { // HMODULE module; // if (GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | // GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, -// reinterpret_cast(frame.address), &module)) { +// reinterpret_cast(frame.address), +// &module)) { // wchar_t modulePath[MAX_PATH]; // if (GetModuleFileNameW(module, modulePath, MAX_PATH) > 0) { // char modPathA[MAX_PATH]; -// WideCharToMultiByte(CP_UTF8, 0, modulePath, -1, modPathA, MAX_PATH, nullptr, nullptr); -// frame.module = modPathA; +// WideCharToMultiByte(CP_UTF8, 0, modulePath, -1, modPathA, +// MAX_PATH, nullptr, nullptr); frame.module = modPathA; // } // } // @@ -294,7 +304,8 @@ class BuiltinBackend : public StackTraceBackend { // symbol->SizeOfStruct = sizeof(SYMBOL_INFO); // // DWORD64 displacement = 0; -// if (SymFromAddr(GetCurrentProcess(), address, &displacement, symbol)) { +// if (SymFromAddr(GetCurrentProcess(), address, &displacement, +// symbol)) { // frame.function = symbol->Name; // frame.offset = static_cast(displacement); // } @@ -304,7 +315,8 @@ class BuiltinBackend : public StackTraceBackend { // line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); // DWORD lineDisplacement = 0; // -// if (SymGetLineFromAddr64(GetCurrentProcess(), address, &lineDisplacement, &line)) { +// if (SymGetLineFromAddr64(GetCurrentProcess(), address, +// &lineDisplacement, &line)) { // frame.sourceFile = line.FileName; // frame.sourceLine = line.LineNumber; // } @@ -315,11 +327,14 @@ class BuiltinBackend : public StackTraceBackend { // // #elif defined(__APPLE__) || defined(__linux__) #if defined(__APPLE__) || defined(__linux__) - void captureUnix(std::vector& frames, const StackTraceConfig& config) { + void captureUnix(std::vector& frames, + const StackTraceConfig& config) { constexpr int MAX_FRAMES = 256; void* framePtrs[MAX_FRAMES]; - int numFrames = backtrace(framePtrs, std::min(config.maxDepth + config.skipFrames, MAX_FRAMES)); + int numFrames = backtrace( + framePtrs, + std::min(config.maxDepth + config.skipFrames, MAX_FRAMES)); if (numFrames <= config.skipFrames) { return; } @@ -358,7 +373,7 @@ class BuiltinBackend : public StackTraceBackend { if (dlInfo.dli_fbase) { frame.offset = reinterpret_cast(frame.address) - - reinterpret_cast(dlInfo.dli_fbase); + reinterpret_cast(dlInfo.dli_fbase); } } @@ -369,7 +384,8 @@ class BuiltinBackend : public StackTraceBackend { // Try to extract function name from symbol string std::regex functionRegex(R"(.*\s+(.+)\s+\+\s+0x[0-9a-f]+)"); std::smatch matches; - if (std::regex_search(symbolStr, matches, functionRegex) && matches.size() > 1) { + if (std::regex_search(symbolStr, matches, functionRegex) && + matches.size() > 1) { frame.function = matches[1].str(); } } @@ -387,7 +403,8 @@ class CpptraceBackend : public StackTraceBackend { std::vector frames; try { - auto trace = cpptrace::generate_trace(config.skipFrames, config.maxDepth); + auto trace = + cpptrace::generate_trace(config.skipFrames, config.maxDepth); frames.reserve(trace.frames.size()); for (const auto& cppFrame : trace.frames) { @@ -407,13 +424,9 @@ class CpptraceBackend : public StackTraceBackend { return frames; } - std::string getName() const override { - return "cpptrace"; - } + std::string getName() const override { return "cpptrace"; } - bool isAvailable() const override { - return true; - } + bool isAvailable() const override { return true; } }; #endif @@ -436,7 +449,8 @@ class BackwardBackend : public StackTraceBackend { printer.color_mode = backward::ColorMode::never; // Skip frames as requested - size_t startIdx = std::min(static_cast(config.skipFrames), st.size()); + size_t startIdx = + std::min(static_cast(config.skipFrames), st.size()); frames.reserve(st.size() - startIdx); for (size_t i = startIdx; i < st.size(); ++i) { @@ -469,13 +483,9 @@ class BackwardBackend : public StackTraceBackend { return frames; } - std::string getName() const override { - return "backward"; - } + std::string getName() const override { return "backward"; } - bool isAvailable() const override { - return true; - } + bool isAvailable() const override { return true; } }; #endif @@ -489,7 +499,8 @@ class BoostBackend : public StackTraceBackend { std::vector frames; try { - auto st = boost::stacktrace::stacktrace(config.skipFrames, config.maxDepth); + auto st = boost::stacktrace::stacktrace(config.skipFrames, + config.maxDepth); frames.reserve(st.size()); for (size_t i = 0; i < st.size(); ++i) { @@ -510,23 +521,20 @@ class BoostBackend : public StackTraceBackend { return frames; } - std::string getName() const override { - return "boost"; - } + std::string getName() const override { return "boost"; } - bool isAvailable() const override { - return true; - } + bool isAvailable() const override { return true; } }; #endif -} // namespace backends +} // namespace backends // ============================================================================ // StackTraceBackendFactory Implementation // ============================================================================ -std::unique_ptr StackTraceBackendFactory::create(const std::string& name) { +std::unique_ptr StackTraceBackendFactory::create( + const std::string& name) { if (name == "auto") { return createBest(); } @@ -599,29 +607,28 @@ std::vector StackTraceBackendFactory::getBackendPriority() { #ifdef ATOM_USE_BOOST_STACKTRACE "boost", #endif - "builtin" - }; + "builtin"}; } // ============================================================================ // StackTrace Implementation // ============================================================================ -StackTrace::StackTrace() : config_(defaultConfig_) { - capture(); -} +StackTrace::StackTrace() : config_(defaultConfig_) { capture(); } StackTrace::StackTrace(const StackTraceConfig& config) : config_(config) { capture(); } StackTrace::StackTrace(const StackTrace& other) - : frames_(other.frames_), backendName_(other.backendName_), config_(other.config_) { -} + : frames_(other.frames_), + backendName_(other.backendName_), + config_(other.config_) {} StackTrace::StackTrace(StackTrace&& other) noexcept - : frames_(std::move(other.frames_)), backendName_(std::move(other.backendName_)), config_(std::move(other.config_)) { -} + : frames_(std::move(other.frames_)), + backendName_(std::move(other.backendName_)), + config_(std::move(other.config_)) {} StackTrace& StackTrace::operator=(const StackTrace& other) { if (this != &other) { @@ -641,9 +648,7 @@ StackTrace& StackTrace::operator=(StackTrace&& other) noexcept { return *this; } -std::string StackTrace::toString() const { - return toString(config_); -} +std::string StackTrace::toString() const { return toString(config_); } std::string StackTrace::toString(const StackTraceConfig& config) const { if (frames_.empty()) { @@ -658,7 +663,8 @@ std::string StackTrace::toString(const StackTraceConfig& config) const { std::string frameStr = frame.toString(config); // Apply frame filter if provided - if (config.frameFilter && !config.frameFilter(frameStr, static_cast(i))) { + if (config.frameFilter && + !config.frameFilter(frameStr, static_cast(i))) { continue; } @@ -669,21 +675,13 @@ std::string StackTrace::toString(const StackTraceConfig& config) const { return config.prettify ? stacktrace_utils::prettify(result) : result; } -const std::vector& StackTrace::getFrames() const { - return frames_; -} +const std::vector& StackTrace::getFrames() const { return frames_; } -size_t StackTrace::size() const { - return frames_.size(); -} +size_t StackTrace::size() const { return frames_.size(); } -bool StackTrace::empty() const { - return frames_.empty(); -} +bool StackTrace::empty() const { return frames_.empty(); } -std::string StackTrace::getBackendName() const { - return backendName_; -} +std::string StackTrace::getBackendName() const { return backendName_; } void StackTrace::setDefaultConfig(const StackTraceConfig& config) { defaultConfig_ = config; @@ -723,7 +721,8 @@ std::unique_ptr StackTrace::getBestBackend() { return StackTraceBackendFactory::createBest(); } -std::unique_ptr StackTrace::createBackend(const std::string& name) { +std::unique_ptr StackTrace::createBackend( + const std::string& name) { return StackTraceBackendFactory::create(name); } @@ -750,6 +749,6 @@ std::string current(const StackTraceConfig& config) { return trace.toString(); } -} // namespace stacktrace +} // namespace stacktrace } // namespace atom::error diff --git a/atom/error/stacktrace.hpp b/atom/error/stacktrace.hpp index 0a70e635..de383f1c 100644 --- a/atom/error/stacktrace.hpp +++ b/atom/error/stacktrace.hpp @@ -1,21 +1,21 @@ #ifndef ATOM_ERROR_STACKTRACE_HPP #define ATOM_ERROR_STACKTRACE_HPP +#include +#include #include #include #include -#include -#include // External stacktrace library detection and configuration #ifdef ATOM_USE_CPPTRACE - #define ATOM_STACKTRACE_BACKEND_CPPTRACE +#define ATOM_STACKTRACE_BACKEND_CPPTRACE #elif defined(ATOM_USE_BACKWARD_CPP) - #define ATOM_STACKTRACE_BACKEND_BACKWARD +#define ATOM_STACKTRACE_BACKEND_BACKWARD #elif defined(ATOM_USE_BOOST_STACKTRACE) - #define ATOM_STACKTRACE_BACKEND_BOOST +#define ATOM_STACKTRACE_BACKEND_BOOST #else - #define ATOM_STACKTRACE_BACKEND_BUILTIN +#define ATOM_STACKTRACE_BACKEND_BUILTIN #endif namespace atom::error { @@ -24,16 +24,18 @@ namespace atom::error { * @brief Configuration options for stacktrace capture and formatting */ struct StackTraceConfig { - int maxDepth = 128; ///< Maximum number of frames to capture - int skipFrames = 1; ///< Number of frames to skip from the top - bool includeAddresses = true; ///< Include memory addresses in output - bool includeModules = true; ///< Include module/library names - bool includeSourceInfo = true; ///< Include source file and line numbers - bool demangle = true; ///< Demangle C++ function names - bool prettify = true; ///< Apply prettification to output - std::string framePrefix = "\t"; ///< Prefix for each frame line - std::string unknownFunction = ""; ///< Placeholder for unknown functions - std::string unknownModule = ""; ///< Placeholder for unknown modules + int maxDepth = 128; ///< Maximum number of frames to capture + int skipFrames = 1; ///< Number of frames to skip from the top + bool includeAddresses = true; ///< Include memory addresses in output + bool includeModules = true; ///< Include module/library names + bool includeSourceInfo = true; ///< Include source file and line numbers + bool demangle = true; ///< Demangle C++ function names + bool prettify = true; ///< Apply prettification to output + std::string framePrefix = "\t"; ///< Prefix for each frame line + std::string unknownFunction = + ""; ///< Placeholder for unknown functions + std::string unknownModule = + ""; ///< Placeholder for unknown modules /** * @brief Custom frame filter function @@ -48,12 +50,12 @@ struct StackTraceConfig { * @brief Information about a single stack frame */ struct StackFrame { - void* address = nullptr; ///< Memory address of the frame - std::string function; ///< Function name (demangled if available) - std::string module; ///< Module/library name - std::string sourceFile; ///< Source file name - int sourceLine = 0; ///< Source line number - uintptr_t offset = 0; ///< Offset within the function/module + void* address = nullptr; ///< Memory address of the frame + std::string function; ///< Function name (demangled if available) + std::string module; ///< Module/library name + std::string sourceFile; ///< Source file name + int sourceLine = 0; ///< Source line number + uintptr_t offset = 0; ///< Offset within the function/module /** * @brief Convert frame to string representation @@ -85,19 +87,19 @@ class StackTraceBackend { }; namespace backends { - class BuiltinBackend; - class CpptraceBackend; - class BackwardBackend; - class BoostBackend; -} +class BuiltinBackend; +class CpptraceBackend; +class BackwardBackend; +class BoostBackend; +} // namespace backends /** * @brief Enhanced stack trace class with support for multiple backends * - * This class provides a unified interface for capturing and formatting stack traces - * using different backend implementations. It supports external libraries like - * cpptrace, backward-cpp, and boost::stacktrace, with fallback to built-in - * platform-specific implementations. + * This class provides a unified interface for capturing and formatting stack + * traces using different backend implementations. It supports external + * libraries like cpptrace, backward-cpp, and boost::stacktrace, with fallback + * to built-in platform-specific implementations. */ class StackTrace { public: @@ -194,7 +196,8 @@ class StackTrace { /** * @brief Force use of specific backend - * @param backendName Name of backend to use ("auto" for automatic selection) + * @param backendName Name of backend to use ("auto" for automatic + * selection) */ static void setPreferredBackend(const std::string& backendName); @@ -219,7 +222,8 @@ class StackTrace { /** * @brief Create backend by name */ - static std::unique_ptr createBackend(const std::string& name); + static std::unique_ptr createBackend( + const std::string& name); }; /** @@ -229,7 +233,8 @@ class StackTraceBackendFactory { public: /** * @brief Create backend by name - * @param name Backend name ("builtin", "cpptrace", "backward", "boost", "auto") + * @param name Backend name ("builtin", "cpptrace", "backward", "boost", + * "auto") * @return Unique pointer to backend, nullptr if not available */ static std::unique_ptr create(const std::string& name); @@ -254,66 +259,66 @@ class StackTraceBackendFactory { * @brief Utility functions for stacktrace processing */ namespace stacktrace_utils { - /** - * @brief Demangle C++ function name - * @param mangled Mangled function name - * @return Demangled function name, or original if demangling fails - */ - std::string demangle(const std::string& mangled); +/** + * @brief Demangle C++ function name + * @param mangled Mangled function name + * @return Demangled function name, or original if demangling fails + */ +std::string demangle(const std::string& mangled); - /** - * @brief Prettify stacktrace output - * @param input Raw stacktrace string - * @return Prettified stacktrace string - */ - std::string prettify(const std::string& input); +/** + * @brief Prettify stacktrace output + * @param input Raw stacktrace string + * @return Prettified stacktrace string + */ +std::string prettify(const std::string& input); - /** - * @brief Format memory address - * @param address Memory address - * @return Formatted address string - */ - std::string formatAddress(uintptr_t address); +/** + * @brief Format memory address + * @param address Memory address + * @return Formatted address string + */ +std::string formatAddress(uintptr_t address); - /** - * @brief Get base name from file path - * @param path Full file path - * @return Base name (filename only) - */ - std::string getBaseName(const std::string& path); +/** + * @brief Get base name from file path + * @param path Full file path + * @return Base name (filename only) + */ +std::string getBaseName(const std::string& path); - /** - * @brief Check if a string contains a mangled C++ name - * @param str String to check - * @return true if string appears to contain mangled names - */ - bool containsMangledNames(const std::string& str); -} +/** + * @brief Check if a string contains a mangled C++ name + * @param str String to check + * @return true if string appears to contain mangled names + */ +bool containsMangledNames(const std::string& str); +} // namespace stacktrace_utils /** * @brief Convenience functions for quick stacktrace capture */ namespace stacktrace { - /** - * @brief Capture current stack trace with default settings - * @return String representation of stack trace - */ - std::string current(); +/** + * @brief Capture current stack trace with default settings + * @return String representation of stack trace + */ +std::string current(); - /** - * @brief Capture current stack trace with custom depth - * @param maxDepth Maximum number of frames to capture - * @return String representation of stack trace - */ - std::string current(int maxDepth); +/** + * @brief Capture current stack trace with custom depth + * @param maxDepth Maximum number of frames to capture + * @return String representation of stack trace + */ +std::string current(int maxDepth); - /** - * @brief Capture current stack trace with custom configuration - * @param config Configuration options - * @return String representation of stack trace - */ - std::string current(const StackTraceConfig& config); -} +/** + * @brief Capture current stack trace with custom configuration + * @param config Configuration options + * @return String representation of stack trace + */ +std::string current(const StackTraceConfig& config); +} // namespace stacktrace } // namespace atom::error diff --git a/atom/extra/asio/CMakeLists.txt b/atom/extra/asio/CMakeLists.txt index 0726f42c..3b274a97 100644 --- a/atom/extra/asio/CMakeLists.txt +++ b/atom/extra/asio/CMakeLists.txt @@ -1,98 +1,85 @@ -# CMakeLists.txt for ASIO networking library extensions -# Integrated with main Atom build system +# CMakeLists.txt for ASIO networking library extensions Integrated with main +# Atom build system # Find required dependencies find_package(Threads REQUIRED) # Set CMake policy for FindBoost module if(POLICY CMP0167) - cmake_policy(SET CMP0167 NEW) + cmake_policy(SET CMP0167 NEW) endif() # Try to find ASIO (standalone or Boost.Asio) find_package(asio QUIET) find_package(Boost QUIET COMPONENTS system) -# Debug output (can be removed in production) -# message(STATUS "asio_FOUND: ${asio_FOUND}") -# message(STATUS "Asio_FOUND: ${Asio_FOUND}") -# message(STATUS "Boost_FOUND: ${Boost_FOUND}") +# Debug output (can be removed in production) message(STATUS "asio_FOUND: +# ${asio_FOUND}") message(STATUS "Asio_FOUND: ${Asio_FOUND}") message(STATUS +# "Boost_FOUND: ${Boost_FOUND}") # Only build if ASIO is available -if(asio_FOUND OR Asio_FOUND OR Boost_FOUND) - # MQTT client source files - set(MQTT_SOURCES - mqtt/client.cpp - mqtt/packet.cpp - ) - - # MQTT client header files - set(MQTT_HEADERS - mqtt/client.hpp - mqtt/packet.hpp - mqtt/protocol.hpp - mqtt/test_client.hpp - mqtt/test_packet.hpp - mqtt/test_protocol.hpp - mqtt/test_types.hpp - mqtt/types.hpp - ) - - # SSE (Server-Sent Events) source files - set(SSE_SOURCES - sse/event.cpp - sse/event_store.cpp - ) - - # SSE header files - set(SSE_HEADERS - sse/event.hpp - sse/event_store.hpp - sse/sse.hpp - ) - - # Main header files - set(ASIO_HEADERS - asio_compatibility.hpp - ) - - # Combine all sources and headers - set(ASIO_ALL_SOURCES ${MQTT_SOURCES} ${SSE_SOURCES}) - set(ASIO_ALL_HEADERS ${MQTT_HEADERS} ${SSE_HEADERS} ${ASIO_HEADERS}) - - # Create library - add_library(atom-extra-asio ${ASIO_ALL_SOURCES} ${ASIO_ALL_HEADERS}) - - # Set target properties - target_include_directories(atom-extra-asio PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) - - # Set target properties for consistency (C++23 required for std::expected) - set_target_properties(atom-extra-asio PROPERTIES - CXX_STANDARD 23 - CXX_STANDARD_REQUIRED ON - CXX_EXTENSIONS OFF - ) - - # Link dependencies - target_link_libraries(atom-extra-asio PUBLIC Threads::Threads) - - if(asio_FOUND) - target_link_libraries(atom-extra-asio PUBLIC asio::asio) - message(STATUS "atom-extra-asio: Built with standalone ASIO (asio_FOUND)") - elseif(Asio_FOUND) - # Handle case where the package is found but with different variable name - if(TARGET asio::asio) - target_link_libraries(atom-extra-asio PUBLIC asio::asio) - endif() - message(STATUS "atom-extra-asio: Built with standalone ASIO (Asio_FOUND)") - elseif(Boost_FOUND) - target_link_libraries(atom-extra-asio PUBLIC Boost::system) - message(STATUS "atom-extra-asio: Built with Boost.Asio") +if(asio_FOUND + OR Asio_FOUND + OR Boost_FOUND) + # MQTT client source files + set(MQTT_SOURCES mqtt/client.cpp mqtt/packet.cpp) + + # MQTT client header files + set(MQTT_HEADERS + mqtt/client.hpp + mqtt/packet.hpp + mqtt/protocol.hpp + mqtt/test_client.hpp + mqtt/test_packet.hpp + mqtt/test_protocol.hpp + mqtt/test_types.hpp + mqtt/types.hpp) + + # SSE (Server-Sent Events) source files + set(SSE_SOURCES sse/event.cpp sse/event_store.cpp) + + # SSE header files + set(SSE_HEADERS sse/event.hpp sse/event_store.hpp sse/sse.hpp) + + # Main header files + set(ASIO_HEADERS asio_compatibility.hpp) + + # Combine all sources and headers + set(ASIO_ALL_SOURCES ${MQTT_SOURCES} ${SSE_SOURCES}) + set(ASIO_ALL_HEADERS ${MQTT_HEADERS} ${SSE_HEADERS} ${ASIO_HEADERS}) + + # Create library + add_library(atom-extra-asio ${ASIO_ALL_SOURCES} ${ASIO_ALL_HEADERS}) + + # Set target properties + target_include_directories(atom-extra-asio PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) + + # Set target properties for consistency (C++23 required for std::expected) + set_target_properties( + atom-extra-asio + PROPERTIES CXX_STANDARD 23 + CXX_STANDARD_REQUIRED ON + CXX_EXTENSIONS OFF) + + # Link dependencies + target_link_libraries(atom-extra-asio PUBLIC Threads::Threads) + + if(asio_FOUND) + target_link_libraries(atom-extra-asio PUBLIC asio::asio) + message(STATUS "atom-extra-asio: Built with standalone ASIO (asio_FOUND)") + elseif(Asio_FOUND) + # Handle case where the package is found but with different variable name + if(TARGET asio::asio) + target_link_libraries(atom-extra-asio PUBLIC asio::asio) endif() - - # Installation rules (optional) - # install(TARGETS atom-extra-asio DESTINATION lib) - # install(FILES ${ASIO_ALL_HEADERS} DESTINATION include/atom/extra/asio) + message(STATUS "atom-extra-asio: Built with standalone ASIO (Asio_FOUND)") + elseif(Boost_FOUND) + target_link_libraries(atom-extra-asio PUBLIC Boost::system) + message(STATUS "atom-extra-asio: Built with Boost.Asio") + endif() + + # Installation rules (optional) install(TARGETS atom-extra-asio DESTINATION + # lib) install(FILES ${ASIO_ALL_HEADERS} DESTINATION include/atom/extra/asio) else() - message(STATUS "atom-extra-asio: Skipped due to missing ASIO dependencies") + message(STATUS "atom-extra-asio: Skipped due to missing ASIO dependencies") endif() diff --git a/atom/extra/asio/mqtt/packet.cpp b/atom/extra/asio/mqtt/packet.cpp index 07ced9b6..001e1d9a 100644 --- a/atom/extra/asio/mqtt/packet.cpp +++ b/atom/extra/asio/mqtt/packet.cpp @@ -376,7 +376,8 @@ Result PacketCodec::parse_publish(const PacketHeader& header, Result> PacketCodec::parse_suback( std::span data) { if (data.size() < 4) { - return Result>::error(ErrorCode::MALFORMED_PACKET); + return Result>::error( + ErrorCode::MALFORMED_PACKET); } BinaryBuffer buffer; @@ -390,7 +391,8 @@ Result> PacketCodec::parse_suback( // Skip properties (MQTT 5.0) auto properties_length_result = buffer.read_variable_int(); if (!properties_length_result) - return Result>::error(properties_length_result.error()); + return Result>::error( + properties_length_result.error()); uint32_t properties_length = *properties_length_result; for (uint32_t i = 0; diff --git a/atom/extra/asio/mqtt/test_packet.hpp b/atom/extra/asio/mqtt/test_packet.hpp index fd2971d5..a1a2cae9 100644 --- a/atom/extra/asio/mqtt/test_packet.hpp +++ b/atom/extra/asio/mqtt/test_packet.hpp @@ -9,7 +9,6 @@ #include "packet.hpp" #include "types.hpp" - using namespace mqtt; TEST(PacketTypeTest, EnumValues) { diff --git a/atom/extra/asio/mqtt/test_types.hpp b/atom/extra/asio/mqtt/test_types.hpp index 2abbcb24..457631ed 100644 --- a/atom/extra/asio/mqtt/test_types.hpp +++ b/atom/extra/asio/mqtt/test_types.hpp @@ -7,7 +7,6 @@ #include #include "types.hpp" - using namespace mqtt; TEST(ProtocolVersionTest, EnumValues) { diff --git a/atom/extra/asio/sse/client/client.hpp b/atom/extra/asio/sse/client/client.hpp index 68617eb0..c7959b81 100644 --- a/atom/extra/asio/sse/client/client.hpp +++ b/atom/extra/asio/sse/client/client.hpp @@ -6,8 +6,8 @@ */ #include "../../asio_compatibility.hpp" -#include "client_config.hpp" #include "../event.hpp" +#include "client_config.hpp" #include #include diff --git a/atom/extra/asio/sse/client/client_config.cpp b/atom/extra/asio/sse/client/client_config.cpp index e9507ecf..1a38db75 100644 --- a/atom/extra/asio/sse/client/client_config.cpp +++ b/atom/extra/asio/sse/client/client_config.cpp @@ -102,4 +102,4 @@ void ClientConfig::save_to_file(const std::string& filename) const { } } -} // namespace sse +} // namespace atom::extra::asio::sse diff --git a/atom/extra/asio/sse/server/auth_service.cpp b/atom/extra/asio/sse/server/auth_service.cpp index 974a968a..72b9852c 100644 --- a/atom/extra/asio/sse/server/auth_service.cpp +++ b/atom/extra/asio/sse/server/auth_service.cpp @@ -3,7 +3,6 @@ #include #include "atom/type/json.hpp" - using json = nlohmann::json; namespace atom::extra::asio::sse { diff --git a/atom/extra/asio/sse/server/connection.hpp b/atom/extra/asio/sse/server/connection.hpp index bba02f15..cfcce905 100644 --- a/atom/extra/asio/sse/server/connection.hpp +++ b/atom/extra/asio/sse/server/connection.hpp @@ -14,7 +14,6 @@ #include "metrics.hpp" #include "server_config.hpp" - #include #include #include diff --git a/atom/extra/asio/sse/server/event_queue.hpp b/atom/extra/asio/sse/server/event_queue.hpp index 6cdc0611..a7259fc2 100644 --- a/atom/extra/asio/sse/server/event_queue.hpp +++ b/atom/extra/asio/sse/server/event_queue.hpp @@ -5,12 +5,12 @@ * @brief Thread-safe event queue for broadcasting */ -#include "../event.hpp" -#include "event_store.hpp" #include #include #include #include +#include "../event.hpp" +#include "event_store.hpp" namespace atom::extra::asio::sse { @@ -33,4 +33,4 @@ class EventQueue { bool persist_events_; }; -} // namespace sse_server +} // namespace atom::extra::asio::sse diff --git a/atom/extra/asio/sse/server/metrics.hpp b/atom/extra/asio/sse/server/metrics.hpp index 818335a4..3b23c5b4 100644 --- a/atom/extra/asio/sse/server/metrics.hpp +++ b/atom/extra/asio/sse/server/metrics.hpp @@ -9,7 +9,6 @@ #include #include "atom/type/json.hpp" - namespace atom::extra::asio::sse { /** diff --git a/atom/extra/asio/sse/server/server_config.cpp b/atom/extra/asio/sse/server/server_config.cpp index 61665594..da98f615 100644 --- a/atom/extra/asio/sse/server/server_config.cpp +++ b/atom/extra/asio/sse/server/server_config.cpp @@ -3,7 +3,6 @@ #include #include - using json = nlohmann::json; namespace atom::extra::asio::sse { diff --git a/atom/extra/asio/sse/sse.hpp b/atom/extra/asio/sse/sse.hpp index 0868efe0..a91c53c6 100644 --- a/atom/extra/asio/sse/sse.hpp +++ b/atom/extra/asio/sse/sse.hpp @@ -6,8 +6,8 @@ */ #include "asio_compatibility.hpp" -#include "event.hpp" +#include "client.hpp" #include "client_config.hpp" +#include "event.hpp" #include "event_store.hpp" -#include "client.hpp" #include "logger.hpp" diff --git a/atom/extra/asio/xmake.lua b/atom/extra/asio/xmake.lua index 879bf282..fc82a503 100644 --- a/atom/extra/asio/xmake.lua +++ b/atom/extra/asio/xmake.lua @@ -51,17 +51,17 @@ local asio_headers = { target("atom-extra-asio") set_kind("static") - + -- Add MQTT sources for _, src in ipairs(mqtt_sources) do add_files(src) end - + -- Add SSE sources for _, src in ipairs(sse_sources) do add_files(src) end - + -- Add all headers for _, hdr in ipairs(mqtt_headers) do add_headerfiles(hdr) @@ -72,19 +72,19 @@ target("atom-extra-asio") for _, hdr in ipairs(asio_headers) do add_headerfiles(hdr) end - + -- Include directories add_includedirs(".", {public = true}) - + -- Add packages add_packages("asio", "boost") - + -- Add system libraries add_syslinks("pthread") - + -- Set C++ standard set_languages("c++23") - + -- Installation on_install(function (target) local installdir = target:installdir() or "$(prefix)" @@ -103,4 +103,3 @@ target("atom-extra-asio") os.cp(hdr, path.join(headerdir, "sse")) end end) - diff --git a/atom/extra/beast/http.cpp b/atom/extra/beast/http.cpp index 9b6114be..0f363aca 100644 --- a/atom/extra/beast/http.cpp +++ b/atom/extra/beast/http.cpp @@ -69,46 +69,50 @@ void HttpClient::gracefulClose() { } } -auto HttpClient::request( - http::verb method, std::string_view host, std::string_view port, - std::string_view target, int version, std::string_view content_type, - std::string_view body, - const std::unordered_map& headers) - -> http::response { +auto HttpClient::request(http::verb method, std::string_view host, + std::string_view port, std::string_view target, + int version, std::string_view content_type, + std::string_view body, + const std::unordered_map& + headers) -> http::response { validateHostPort(host, port); // Use manual timeout mechanism for reliable timeout handling - auto request_future = std::async(std::launch::async, [this, method, host, port, target, version, content_type, body, &headers]() -> http::response { - http::request req; - setupRequest(req, method, host, target, version, content_type, body, - headers); - - spdlog::debug("Sending {} request to {}:{}{}", - std::string(http::to_string(method)), host, port, target); + auto request_future = std::async( + std::launch::async, + [this, method, host, port, target, version, content_type, body, + &headers]() -> http::response { + http::request req; + setupRequest(req, method, host, target, version, content_type, body, + headers); + + spdlog::debug("Sending {} request to {}:{}{}", + std::string(http::to_string(method)), host, port, + target); - auto const results = - resolver_.resolve(std::string(host), std::string(port)); + auto const results = + resolver_.resolve(std::string(host), std::string(port)); - // Set timeout before connect - stream_.expires_after(timeout_); - stream_.connect(results); + // Set timeout before connect + stream_.expires_after(timeout_); + stream_.connect(results); - // Set timeout before write - stream_.expires_after(timeout_); - http::write(stream_, req); + // Set timeout before write + stream_.expires_after(timeout_); + http::write(stream_, req); - // Set timeout before read - this is critical for timeout handling - stream_.expires_after(timeout_); - beast::flat_buffer buffer; - http::response res; - http::read(stream_, buffer, res); + // Set timeout before read - this is critical for timeout handling + stream_.expires_after(timeout_); + beast::flat_buffer buffer; + http::response res; + http::read(stream_, buffer, res); - spdlog::debug("Received response: {} {}", static_cast(res.result()), - res.reason()); + spdlog::debug("Received response: {} {}", + static_cast(res.result()), res.reason()); - gracefulClose(); - return res; - }); + gracefulClose(); + return res; + }); // Wait for the request with timeout if (request_future.wait_for(timeout_) == std::future_status::timeout) { @@ -214,7 +218,8 @@ void HttpClient::downloadFile(std::string_view host, std::string_view port, std::error_code ec; if (!std::filesystem::exists(parent, ec) && !std::filesystem::create_directories(parent, ec)) { - throw std::runtime_error("Failed to create directory: " + parent.string()); + throw std::runtime_error("Failed to create directory: " + + parent.string()); } } @@ -244,22 +249,21 @@ auto HttpClient::requestWithRetry( for (int attempt = 0; attempt < retry_count; ++attempt) { try { spdlog::debug("Request attempt {} of {}", attempt + 1, retry_count); - auto response = request(method, host, port, target, version, content_type, - body, headers); + auto response = request(method, host, port, target, version, + content_type, body, headers); // Check if we should retry based on status code if (response.result() == http::status::service_unavailable || response.result() == http::status::bad_gateway || response.result() == http::status::gateway_timeout) { - if (attempt + 1 == retry_count) { spdlog::error("All retry attempts failed with status: {}", - static_cast(response.result())); - return response; // Return the last response + static_cast(response.result())); + return response; // Return the last response } spdlog::warn("Request attempt {} failed with status: {}", - attempt + 1, static_cast(response.result())); + attempt + 1, static_cast(response.result())); auto delay = std::chrono::milliseconds(100 << attempt); std::this_thread::sleep_for(delay); continue; diff --git a/atom/extra/beast/http.hpp b/atom/extra/beast/http.hpp index 7e64587a..dfd9848b 100644 --- a/atom/extra/beast/http.hpp +++ b/atom/extra/beast/http.hpp @@ -104,12 +104,12 @@ class HttpClient : public std::enable_shared_from_this { * @throws std::invalid_argument If host or port is empty * @throws beast::system_error On connection or request failure */ - auto request( - http::verb method, std::string_view host, std::string_view port, - std::string_view target, int version = 11, - std::string_view content_type = "", std::string_view body = "", - const std::unordered_map& headers = {}) - -> http::response; + auto request(http::verb method, std::string_view host, + std::string_view port, std::string_view target, + int version = 11, std::string_view content_type = "", + std::string_view body = "", + const std::unordered_map& headers = + {}) -> http::response; /** * @brief Sends an asynchronous HTTP request with callback diff --git a/atom/extra/beast/http_utils.hpp b/atom/extra/beast/http_utils.hpp index 15145e3e..d4c9846e 100644 --- a/atom/extra/beast/http_utils.hpp +++ b/atom/extra/beast/http_utils.hpp @@ -196,9 +196,12 @@ inline std::string urlEncode(std::string_view input) { static const auto unreserved = []() { std::array table{}; // Unreserved characters: ALPHA / DIGIT / "-" / "." / "_" / "~" - for (char c = 'A'; c <= 'Z'; ++c) table[static_cast(c)] = true; - for (char c = 'a'; c <= 'z'; ++c) table[static_cast(c)] = true; - for (char c = '0'; c <= '9'; ++c) table[static_cast(c)] = true; + for (char c = 'A'; c <= 'Z'; ++c) + table[static_cast(c)] = true; + for (char c = 'a'; c <= 'z'; ++c) + table[static_cast(c)] = true; + for (char c = '0'; c <= '9'; ++c) + table[static_cast(c)] = true; table[static_cast('-')] = true; table[static_cast('.')] = true; table[static_cast('_')] = true; @@ -751,8 +754,8 @@ class HeaderUtils { static std::pair parseContentType( std::string_view content_type_header) { size_t semicolon_pos = content_type_header.find(';'); - std::string content_type = std::string( - trim(content_type_header.substr(0, semicolon_pos))); + std::string content_type = + std::string(trim(content_type_header.substr(0, semicolon_pos))); std::string charset; if (semicolon_pos != std::string_view::npos) { @@ -762,8 +765,7 @@ class HeaderUtils { if (charset_pos != std::string_view::npos) { std::string_view charset_part = params.substr(charset_pos + 8); size_t end_pos = charset_part.find_first_of(" ;"); - charset = std::string( - trim(charset_part.substr(0, end_pos))); + charset = std::string(trim(charset_part.substr(0, end_pos))); // Remove quotes if present if (charset.size() >= 2 && charset.front() == '"' && diff --git a/atom/extra/beast/ws.cpp b/atom/extra/beast/ws.cpp index 9cb8d3f5..72f16517 100644 --- a/atom/extra/beast/ws.cpp +++ b/atom/extra/beast/ws.cpp @@ -270,7 +270,8 @@ void WSClient::startPing() { ping_timer_->expires_after(ping_interval_); ping_timer_->async_wait(net::bind_executor( ws_->get_executor(), - [this, self = shared_from_this()](beast::error_code timer_ec) { + [this, self = shared_from_this()]( + beast::error_code timer_ec) { if (!timer_ec && is_connected_) { startPing(); } diff --git a/atom/extra/beast/xmake.lua b/atom/extra/beast/xmake.lua index b25d3f3f..6f32f5ba 100644 --- a/atom/extra/beast/xmake.lua +++ b/atom/extra/beast/xmake.lua @@ -30,25 +30,25 @@ local headers = { target("atom-extra-beast") set_kind("static") - + -- Add files for _, src in ipairs(sources) do add_files(src) end - + for _, hdr in ipairs(headers) do add_headerfiles(hdr) end - + -- Include directories add_includedirs(".", {public = true}) - + -- Add packages add_packages("boost", "nlohmann_json", "fmt", "spdlog") - + -- Set C++ standard set_languages("c++20") - + -- Installation on_install(function (target) local installdir = target:installdir() or "$(prefix)" @@ -59,4 +59,3 @@ target("atom-extra-beast") os.cp(hdr, headerdir) end end) - diff --git a/atom/extra/boost/uuid.hpp b/atom/extra/boost/uuid.hpp index e7558e7a..84c320b9 100644 --- a/atom/extra/boost/uuid.hpp +++ b/atom/extra/boost/uuid.hpp @@ -62,17 +62,14 @@ class UUID { * @brief Checks if UUID is nil (all zeros) * @return True if UUID is nil */ - [[nodiscard]] bool isNil() const noexcept { - return uuid_.is_nil(); - } + [[nodiscard]] bool isNil() const noexcept { return uuid_.is_nil(); } /** * @brief Three-way comparison operator * @param other UUID to compare with * @return Comparison result */ - std::strong_ordering operator<=>( - const UUID& other) const noexcept { + std::strong_ordering operator<=>(const UUID& other) const noexcept { if (uuid_ < other.uuid_) [[likely]] { return std::strong_ordering::less; } @@ -183,17 +180,13 @@ class UUID { * @brief Gets UUID version * @return Version number */ - [[nodiscard]] int version() const noexcept { - return uuid_.version(); - } + [[nodiscard]] int version() const noexcept { return uuid_.version(); } /** * @brief Gets UUID variant * @return Variant number */ - [[nodiscard]] int variant() const noexcept { - return uuid_.variant(); - } + [[nodiscard]] int variant() const noexcept { return uuid_.variant(); } /** * @brief Generates version 1 (timestamp-based) UUID diff --git a/atom/extra/boost/xmake.lua b/atom/extra/boost/xmake.lua index 5b2f685b..a58fe481 100644 --- a/atom/extra/boost/xmake.lua +++ b/atom/extra/boost/xmake.lua @@ -24,21 +24,21 @@ local headers = { target("atom-extra-boost") set_kind("headeronly") - + -- Add headers for _, hdr in ipairs(headers) do add_headerfiles(hdr) end - + -- Include directories add_includedirs(".", {public = true}) - + -- Add packages add_packages("boost") - + -- Set C++ standard set_languages("c++20") - + -- Installation on_install(function (target) local installdir = target:installdir() or "$(prefix)" @@ -48,4 +48,3 @@ target("atom-extra-boost") os.cp(hdr, headerdir) end end) - diff --git a/atom/extra/curl/CMakeLists.txt b/atom/extra/curl/CMakeLists.txt index 129b463d..3f132263 100644 --- a/atom/extra/curl/CMakeLists.txt +++ b/atom/extra/curl/CMakeLists.txt @@ -1,5 +1,5 @@ -# CMakeLists.txt for CURL HTTP client library -# Integrated with main Atom build system +# CMakeLists.txt for CURL HTTP client library Integrated with main Atom build +# system # Source files set(CURL_SOURCES @@ -14,8 +14,7 @@ set(CURL_SOURCES response.cpp session.cpp session_pool.cpp - websocket.cpp -) + websocket.cpp) # Header files set(CURL_HEADERS @@ -32,8 +31,7 @@ set(CURL_HEADERS rest_client.hpp session.hpp session_pool.hpp - websocket.hpp -) + websocket.hpp) # Create library add_library(atom-extra-curl ${CURL_SOURCES} ${CURL_HEADERS}) @@ -42,21 +40,21 @@ add_library(atom-extra-curl ${CURL_SOURCES} ${CURL_HEADERS}) target_include_directories(atom-extra-curl PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) # Set target properties for consistency -set_target_properties(atom-extra-curl PROPERTIES - CXX_STANDARD 20 - CXX_STANDARD_REQUIRED ON - CXX_EXTENSIONS OFF -) +set_target_properties( + atom-extra-curl + PROPERTIES CXX_STANDARD 20 + CXX_STANDARD_REQUIRED ON + CXX_EXTENSIONS OFF) # Find and link CURL library (optional) find_package(CURL QUIET) if(CURL_FOUND) - target_link_libraries(atom-extra-curl PUBLIC CURL::libcurl) - message(STATUS "Found CURL, linking to atom-extra-curl") + target_link_libraries(atom-extra-curl PUBLIC CURL::libcurl) + message(STATUS "Found CURL, linking to atom-extra-curl") else() - message(STATUS "CURL not found, atom-extra-curl may have limited functionality") + message( + STATUS "CURL not found, atom-extra-curl may have limited functionality") endif() -# Installation rules (optional) -# install(TARGETS atom-extra-curl DESTINATION lib) +# Installation rules (optional) install(TARGETS atom-extra-curl DESTINATION lib) # install(FILES ${CURL_HEADERS} DESTINATION include/atom/extra/curl) diff --git a/atom/extra/curl/xmake.lua b/atom/extra/curl/xmake.lua index 00c4f606..83565eea 100644 --- a/atom/extra/curl/xmake.lua +++ b/atom/extra/curl/xmake.lua @@ -48,25 +48,25 @@ local headers = { target("atom-extra-curl") set_kind("static") - + -- Add files for _, src in ipairs(sources) do add_files(src) end - + for _, hdr in ipairs(headers) do add_headerfiles(hdr) end - + -- Include directories add_includedirs(".", {public = true}) - + -- Add packages add_packages("libcurl") - + -- Set C++ standard set_languages("c++20") - + -- Installation on_install(function (target) local installdir = target:installdir() or "$(prefix)" @@ -77,4 +77,3 @@ target("atom-extra-curl") os.cp(hdr, headerdir) end end) - diff --git a/atom/extra/dotenv/CMakeLists.txt b/atom/extra/dotenv/CMakeLists.txt index 97605e82..ba3192d7 100644 --- a/atom/extra/dotenv/CMakeLists.txt +++ b/atom/extra/dotenv/CMakeLists.txt @@ -1,13 +1,7 @@ -# CMakeLists.txt for dotenv library -# Integrated with main Atom build system +# CMakeLists.txt for dotenv library Integrated with main Atom build system # Source files -set(DOTENV_SOURCES - dotenv.cpp - parser.cpp - validator.cpp - loader.cpp -) +set(DOTENV_SOURCES dotenv.cpp parser.cpp validator.cpp loader.cpp) # Header files set(DOTENV_HEADERS @@ -17,8 +11,7 @@ set(DOTENV_HEADERS loader.hpp exceptions.hpp test_dotenv.hpp - test_validator.hpp -) + test_validator.hpp) # Create library add_library(atom-extra-dotenv ${DOTENV_SOURCES} ${DOTENV_HEADERS}) @@ -27,11 +20,11 @@ add_library(atom-extra-dotenv ${DOTENV_SOURCES} ${DOTENV_HEADERS}) target_include_directories(atom-extra-dotenv PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) # Set target properties for consistency -set_target_properties(atom-extra-dotenv PROPERTIES - CXX_STANDARD 20 - CXX_STANDARD_REQUIRED ON - CXX_EXTENSIONS OFF -) +set_target_properties( + atom-extra-dotenv + PROPERTIES CXX_STANDARD 20 + CXX_STANDARD_REQUIRED ON + CXX_EXTENSIONS OFF) # Find required packages find_package(Threads REQUIRED) @@ -39,9 +32,8 @@ target_link_libraries(atom-extra-dotenv PUBLIC Threads::Threads) # Platform-specific libraries if(WIN32) - target_link_libraries(atom-extra-dotenv PUBLIC ws2_32) + target_link_libraries(atom-extra-dotenv PUBLIC ws2_32) endif() -# Installation rules (optional) -# install(TARGETS atom-extra-dotenv DESTINATION lib) -# install(FILES ${DOTENV_HEADERS} DESTINATION include/atom/extra/dotenv) +# Installation rules (optional) install(TARGETS atom-extra-dotenv DESTINATION +# lib) install(FILES ${DOTENV_HEADERS} DESTINATION include/atom/extra/dotenv) diff --git a/atom/extra/dotenv/parser.cpp b/atom/extra/dotenv/parser.cpp index 7136bb3c..311f91d3 100644 --- a/atom/extra/dotenv/parser.cpp +++ b/atom/extra/dotenv/parser.cpp @@ -85,7 +85,8 @@ Parser::EnvEntries Parser::parseDetailed(const std::string& content) { return result; } -std::string Parser::processLine(const std::string& line, size_t /*line_number*/) { +std::string Parser::processLine(const std::string& line, + size_t /*line_number*/) { if (isComment(line) || isEmpty(line)) { return ""; } diff --git a/atom/extra/dotenv/xmake.lua b/atom/extra/dotenv/xmake.lua index 1843cf9a..0747233b 100644 --- a/atom/extra/dotenv/xmake.lua +++ b/atom/extra/dotenv/xmake.lua @@ -30,30 +30,30 @@ local headers = { target("atom-extra-dotenv") set_kind("static") - + -- Add files for _, src in ipairs(sources) do add_files(src) end - + for _, hdr in ipairs(headers) do add_headerfiles(hdr) end - + -- Include directories add_includedirs(".", {public = true}) - + -- Add system libraries add_syslinks("pthread") - + -- Windows-specific libraries if is_plat("windows") then add_syslinks("ws2_32") end - + -- Set C++ standard set_languages("c++20") - + -- Installation on_install(function (target) local installdir = target:installdir() or "$(prefix)" @@ -64,4 +64,3 @@ target("atom-extra-dotenv") os.cp(hdr, headerdir) end end) - diff --git a/atom/extra/iconv/CMakeLists.txt b/atom/extra/iconv/CMakeLists.txt index f685c32c..82a48b3d 100644 --- a/atom/extra/iconv/CMakeLists.txt +++ b/atom/extra/iconv/CMakeLists.txt @@ -1,21 +1,17 @@ -# CMakeLists.txt for iconv C++ wrapper -# Integrated with main Atom build system +# CMakeLists.txt for iconv C++ wrapper Integrated with main Atom build system # Header files -set(ICONV_HEADERS - iconv_cpp.hpp -) +set(ICONV_HEADERS iconv_cpp.hpp) # Test source files (optional) -set(ICONV_TEST_SOURCES - test_iconv_cpp.cpp -) +set(ICONV_TEST_SOURCES test_iconv_cpp.cpp) # Create interface library (header-only) add_library(atom-extra-iconv INTERFACE) # Set target properties -target_include_directories(atom-extra-iconv INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) +target_include_directories(atom-extra-iconv + INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) # Set target properties for consistency target_compile_features(atom-extra-iconv INTERFACE cxx_std_20) @@ -23,18 +19,18 @@ target_compile_features(atom-extra-iconv INTERFACE cxx_std_20) # Find and link iconv library (optional) find_package(Iconv QUIET) if(Iconv_FOUND) - target_link_libraries(atom-extra-iconv INTERFACE Iconv::Iconv) - message(STATUS "Found Iconv, linking to atom-extra-iconv") + target_link_libraries(atom-extra-iconv INTERFACE Iconv::Iconv) + message(STATUS "Found Iconv, linking to atom-extra-iconv") else() - message(STATUS "Iconv not found, atom-extra-iconv may have limited functionality") + message( + STATUS "Iconv not found, atom-extra-iconv may have limited functionality") endif() # Optional: Create test executable if(BUILD_TESTING) - add_executable(atom-extra-iconv-test ${ICONV_TEST_SOURCES}) - target_link_libraries(atom-extra-iconv-test atom-extra-iconv) + add_executable(atom-extra-iconv-test ${ICONV_TEST_SOURCES}) + target_link_libraries(atom-extra-iconv-test atom-extra-iconv) endif() -# Installation rules (optional) -# install(TARGETS atom-extra-iconv DESTINATION lib) -# install(FILES ${ICONV_HEADERS} DESTINATION include/atom/extra/iconv) +# Installation rules (optional) install(TARGETS atom-extra-iconv DESTINATION +# lib) install(FILES ${ICONV_HEADERS} DESTINATION include/atom/extra/iconv) diff --git a/atom/extra/iconv/xmake.lua b/atom/extra/iconv/xmake.lua index 04283c30..c280da1c 100644 --- a/atom/extra/iconv/xmake.lua +++ b/atom/extra/iconv/xmake.lua @@ -19,21 +19,21 @@ local headers = { target("atom-extra-iconv") set_kind("headeronly") - + -- Add headers for _, hdr in ipairs(headers) do add_headerfiles(hdr) end - + -- Include directories add_includedirs(".", {public = true}) - + -- Add packages add_packages("libiconv") - + -- Set C++ standard set_languages("c++20") - + -- Installation on_install(function (target) local installdir = target:installdir() or "$(prefix)" @@ -43,4 +43,3 @@ target("atom-extra-iconv") os.cp(hdr, headerdir) end end) - diff --git a/atom/extra/inicpp/CMakeLists.txt b/atom/extra/inicpp/CMakeLists.txt index 9b967955..cba4ce6a 100644 --- a/atom/extra/inicpp/CMakeLists.txt +++ b/atom/extra/inicpp/CMakeLists.txt @@ -1,6 +1,5 @@ -# CMakeLists.txt for INI file parser library -# Integrated with main Atom build system -# Header-only library +# CMakeLists.txt for INI file parser library Integrated with main Atom build +# system Header-only library # Header files set(INICPP_HEADERS @@ -12,18 +11,17 @@ set(INICPP_HEADERS format_converter.hpp inicpp.hpp path_query.hpp - section.hpp -) + section.hpp) # Create interface library (header-only) add_library(atom-extra-inicpp INTERFACE) # Set target properties -target_include_directories(atom-extra-inicpp INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) +target_include_directories(atom-extra-inicpp + INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) # Set target properties for consistency target_compile_features(atom-extra-inicpp INTERFACE cxx_std_20) -# Installation rules (optional) -# install(TARGETS atom-extra-inicpp DESTINATION lib) -# install(FILES ${INICPP_HEADERS} DESTINATION include/atom/extra/inicpp) +# Installation rules (optional) install(TARGETS atom-extra-inicpp DESTINATION +# lib) install(FILES ${INICPP_HEADERS} DESTINATION include/atom/extra/inicpp) diff --git a/atom/extra/inicpp/common.hpp b/atom/extra/inicpp/common.hpp index 27495475..ad55f76c 100644 --- a/atom/extra/inicpp/common.hpp +++ b/atom/extra/inicpp/common.hpp @@ -199,8 +199,8 @@ struct StringInsensitiveLess { * @param rhs The right-hand side string view. * @return True if lhs is less than rhs, false otherwise. */ - auto operator()(std::string_view lhs, std::string_view rhs) const noexcept - -> bool { + auto operator()(std::string_view lhs, + std::string_view rhs) const noexcept -> bool { return std::ranges::lexicographical_compare( lhs, rhs, [](unsigned char a, unsigned char b) noexcept { return std::tolower(a) < std::tolower(b); diff --git a/atom/extra/inicpp/section.hpp b/atom/extra/inicpp/section.hpp index ebc8c440..2b648426 100644 --- a/atom/extra/inicpp/section.hpp +++ b/atom/extra/inicpp/section.hpp @@ -27,8 +27,6 @@ inline auto splitPath(const std::string& path) -> std::vector { return parts; } - - #if INICPP_CONFIG_EVENT_LISTENERS /** * @brief Event types for section events. diff --git a/atom/extra/inicpp/xmake.lua b/atom/extra/inicpp/xmake.lua index dda93bfb..8d4f6d29 100644 --- a/atom/extra/inicpp/xmake.lua +++ b/atom/extra/inicpp/xmake.lua @@ -24,18 +24,18 @@ local headers = { target("atom-extra-inicpp") set_kind("headeronly") - + -- Add headers for _, hdr in ipairs(headers) do add_headerfiles(hdr) end - + -- Include directories add_includedirs(".", {public = true}) - + -- Set C++ standard set_languages("c++20") - + -- Installation on_install(function (target) local installdir = target:installdir() or "$(prefix)" @@ -45,4 +45,3 @@ target("atom-extra-inicpp") os.cp(hdr, headerdir) end end) - diff --git a/atom/extra/injection/CMakeLists.txt b/atom/extra/injection/CMakeLists.txt index 311467d9..5b897c7a 100644 --- a/atom/extra/injection/CMakeLists.txt +++ b/atom/extra/injection/CMakeLists.txt @@ -1,6 +1,5 @@ -# CMakeLists.txt for Dependency Injection library -# Integrated with main Atom build system -# Header-only library +# CMakeLists.txt for Dependency Injection library Integrated with main Atom +# build system Header-only library # Header files set(INJECTION_HEADERS @@ -10,18 +9,18 @@ set(INJECTION_HEADERS container.hpp inject.hpp resolver.hpp - test_all.hpp -) + test_all.hpp) # Create interface library (header-only) add_library(atom-extra-injection INTERFACE) # Set target properties -target_include_directories(atom-extra-injection INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) +target_include_directories(atom-extra-injection + INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) # Set target properties for consistency target_compile_features(atom-extra-injection INTERFACE cxx_std_20) -# Installation rules (optional) -# install(TARGETS atom-extra-injection DESTINATION lib) -# install(FILES ${INJECTION_HEADERS} DESTINATION include/atom/extra/injection) +# Installation rules (optional) install(TARGETS atom-extra-injection DESTINATION +# lib) install(FILES ${INJECTION_HEADERS} DESTINATION +# include/atom/extra/injection) diff --git a/atom/extra/injection/xmake.lua b/atom/extra/injection/xmake.lua index bb196fac..9ec16321 100644 --- a/atom/extra/injection/xmake.lua +++ b/atom/extra/injection/xmake.lua @@ -22,18 +22,18 @@ local headers = { target("atom-extra-injection") set_kind("headeronly") - + -- Add headers for _, hdr in ipairs(headers) do add_headerfiles(hdr) end - + -- Include directories add_includedirs(".", {public = true}) - + -- Set C++ standard set_languages("c++20") - + -- Installation on_install(function (target) local installdir = target:installdir() or "$(prefix)" @@ -43,4 +43,3 @@ target("atom-extra-injection") os.cp(hdr, headerdir) end end) - diff --git a/atom/extra/pugixml/xmake.lua b/atom/extra/pugixml/xmake.lua index 4edbc72c..5490af3a 100644 --- a/atom/extra/pugixml/xmake.lua +++ b/atom/extra/pugixml/xmake.lua @@ -23,21 +23,21 @@ local headers = { target("atom-extra-pugixml") set_kind("headeronly") - + -- Add headers for _, hdr in ipairs(headers) do add_headerfiles(hdr) end - + -- Include directories add_includedirs(".", {public = true}) - + -- Add packages add_packages("pugixml") - + -- Set C++ standard set_languages("c++20") - + -- Installation on_install(function (target) local installdir = target:installdir() or "$(prefix)" @@ -47,4 +47,3 @@ target("atom-extra-pugixml") os.cp(hdr, headerdir) end end) - diff --git a/atom/extra/spdlog/CMakeLists.txt b/atom/extra/spdlog/CMakeLists.txt index 827a019d..4f7a35b2 100644 --- a/atom/extra/spdlog/CMakeLists.txt +++ b/atom/extra/spdlog/CMakeLists.txt @@ -1,5 +1,5 @@ -# CMakeLists.txt for modern logging library (spdlog wrapper) -# Integrated with main Atom build system +# CMakeLists.txt for modern logging library (spdlog wrapper) Integrated with +# main Atom build system # Source files set(SPDLOG_SOURCES @@ -12,13 +12,10 @@ set(SPDLOG_SOURCES utils/timer.cpp utils/archiver.cpp logger/logger.cpp - logger/manager.cpp -) + logger/manager.cpp) # Header files (main header) -set(SPDLOG_HEADERS - modern_log.h -) +set(SPDLOG_HEADERS modern_log.h) # Create library add_library(atom-extra-spdlog ${SPDLOG_SOURCES} ${SPDLOG_HEADERS}) @@ -27,27 +24,25 @@ add_library(atom-extra-spdlog ${SPDLOG_SOURCES} ${SPDLOG_HEADERS}) target_include_directories(atom-extra-spdlog PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) # Set target properties for consistency -set_target_properties(atom-extra-spdlog PROPERTIES - CXX_STANDARD 23 - CXX_STANDARD_REQUIRED ON - CXX_EXTENSIONS OFF -) +set_target_properties( + atom-extra-spdlog + PROPERTIES CXX_STANDARD 23 + CXX_STANDARD_REQUIRED ON + CXX_EXTENSIONS OFF) # Find and link required packages (optional) find_package(spdlog QUIET) find_package(fmt QUIET) if(spdlog_FOUND AND fmt_FOUND) - target_link_libraries(atom-extra-spdlog - PUBLIC - spdlog::spdlog - fmt::fmt - ) - message(STATUS "Found spdlog and fmt, linking to atom-extra-spdlog") + target_link_libraries(atom-extra-spdlog PUBLIC spdlog::spdlog fmt::fmt) + message(STATUS "Found spdlog and fmt, linking to atom-extra-spdlog") else() - message(STATUS "spdlog or fmt not found, atom-extra-spdlog may have limited functionality") + message( + STATUS + "spdlog or fmt not found, atom-extra-spdlog may have limited functionality" + ) endif() -# Installation rules (optional) -# install(TARGETS atom-extra-spdlog DESTINATION lib) -# install(FILES ${SPDLOG_HEADERS} DESTINATION include/atom/extra/spdlog) +# Installation rules (optional) install(TARGETS atom-extra-spdlog DESTINATION +# lib) install(FILES ${SPDLOG_HEADERS} DESTINATION include/atom/extra/spdlog) diff --git a/atom/extra/spdlog/core/concepts.h b/atom/extra/spdlog/core/concepts.h index 69baa2be..94011c08 100644 --- a/atom/extra/spdlog/core/concepts.h +++ b/atom/extra/spdlog/core/concepts.h @@ -4,7 +4,6 @@ #include #include - /** * @file concepts.h * @brief Concepts for formatting, serialization, logging, event handling, and diff --git a/atom/extra/spdlog/core/error.h b/atom/extra/spdlog/core/error.h index 5584a792..bfc7fca3 100644 --- a/atom/extra/spdlog/core/error.h +++ b/atom/extra/spdlog/core/error.h @@ -10,31 +10,30 @@ #include namespace modern_log { - template - class Result { - private: - std::variant data_; +template +class Result { +private: + std::variant data_; - public: - Result(const T& value) : data_(value) {} - Result(T&& value) : data_(std::move(value)) {} - Result(const E& error) : data_(error) {} - Result(E&& error) : data_(std::move(error)) {} - - bool has_value() const { return std::holds_alternative(data_); } - operator bool() const { return has_value(); } +public: + Result(const T& value) : data_(value) {} + Result(T&& value) : data_(std::move(value)) {} + Result(const E& error) : data_(error) {} + Result(E&& error) : data_(std::move(error)) {} - const T& value() const { return std::get(data_); } - T& value() { return std::get(data_); } + bool has_value() const { return std::holds_alternative(data_); } + operator bool() const { return has_value(); } - const E& error() const { return std::get(data_); } - E& error() { return std::get(data_); } + const T& value() const { return std::get(data_); } + T& value() { return std::get(data_); } - const T& operator*() const { return value(); } - T& operator*() { return value(); } - }; -} + const E& error() const { return std::get(data_); } + E& error() { return std::get(data_); } + const T& operator*() const { return value(); } + T& operator*() { return value(); } +}; +} // namespace modern_log namespace modern_log { diff --git a/atom/extra/spdlog/core/test_context.h b/atom/extra/spdlog/core/test_context.h index 6768bc17..abf242c5 100644 --- a/atom/extra/spdlog/core/test_context.h +++ b/atom/extra/spdlog/core/test_context.h @@ -3,7 +3,6 @@ #include #include "context.h" - using modern_log::LogContext; TEST(LogContextTest, DefaultIsEmpty) { diff --git a/atom/extra/spdlog/core/test_error.h b/atom/extra/spdlog/core/test_error.h index bfaf3fd2..df02cf84 100644 --- a/atom/extra/spdlog/core/test_error.h +++ b/atom/extra/spdlog/core/test_error.h @@ -5,7 +5,6 @@ #include #include "error.h" - using modern_log::log_error_category; using modern_log::LogError; using modern_log::LogErrorCategory; diff --git a/atom/extra/spdlog/events/event_system.h b/atom/extra/spdlog/events/event_system.h index 1d5dff94..52337f75 100644 --- a/atom/extra/spdlog/events/event_system.h +++ b/atom/extra/spdlog/events/event_system.h @@ -7,7 +7,6 @@ #include #include "../core/types.h" - namespace modern_log { /** diff --git a/atom/extra/spdlog/events/test_event_system.cpp b/atom/extra/spdlog/events/test_event_system.cpp index 4cdd4e51..2ff36da6 100644 --- a/atom/extra/spdlog/events/test_event_system.cpp +++ b/atom/extra/spdlog/events/test_event_system.cpp @@ -8,7 +8,6 @@ #include "../core/types.h" #include "event_system.h" - using modern_log::LogEvent; using modern_log::LogEventSystem; diff --git a/atom/extra/spdlog/filters/builtin_filters.h b/atom/extra/spdlog/filters/builtin_filters.h index 1f349ea4..e3e777b8 100644 --- a/atom/extra/spdlog/filters/builtin_filters.h +++ b/atom/extra/spdlog/filters/builtin_filters.h @@ -5,7 +5,6 @@ #include #include - namespace modern_log { /** diff --git a/atom/extra/spdlog/logger/manager.cpp b/atom/extra/spdlog/logger/manager.cpp index 5a0bc778..03c184cb 100644 --- a/atom/extra/spdlog/logger/manager.cpp +++ b/atom/extra/spdlog/logger/manager.cpp @@ -127,7 +127,10 @@ Logger& LogManager::default_logger() { Result, LogError> LogManager::create_simple_logger( const std::string& name, Level level, bool console) { - LogConfig config{.name = name, .level = level, .file_config = {}, .console_output = console}; + LogConfig config{.name = name, + .level = level, + .file_config = {}, + .console_output = console}; return instance().create_logger(config); } diff --git a/atom/extra/spdlog/logger/manager.h b/atom/extra/spdlog/logger/manager.h index a3d0a2c5..564a62bb 100644 --- a/atom/extra/spdlog/logger/manager.h +++ b/atom/extra/spdlog/logger/manager.h @@ -6,7 +6,6 @@ #include "../utils/archiver.h" #include "logger.h" - #include #include #include @@ -14,13 +13,11 @@ #include #include - #include #include #include #include - namespace modern_log { /** @@ -75,14 +72,16 @@ class LogManager { * @param config LogConfig structure describing logger settings. * @return Result containing the created Logger or an error. */ - Result, LogError> create_logger(const LogConfig& config); + Result, LogError> create_logger( + const LogConfig& config); /** * @brief Retrieve a logger by name. * @param name Name of the logger. * @return Result containing the Logger or an error if not found. */ - Result, LogError> get_logger(const std::string& name); + Result, LogError> get_logger( + const std::string& name); /** * @brief Remove a logger by name. diff --git a/atom/extra/spdlog/logger/test_logger.cpp b/atom/extra/spdlog/logger/test_logger.cpp index f15202b1..09e6a60e 100644 --- a/atom/extra/spdlog/logger/test_logger.cpp +++ b/atom/extra/spdlog/logger/test_logger.cpp @@ -1,18 +1,18 @@ -#include #include +#include +#include +#include #include +#include #include #include -#include -#include -#include #include "logger.h" using ::testing::_; +using ::testing::InSequence; +using ::testing::NiceMock; using ::testing::Return; using ::testing::StrictMock; -using ::testing::NiceMock; -using ::testing::InSequence; using namespace modern_log; // Mock classes for testing @@ -20,15 +20,20 @@ class MockLogEventSystem : public LogEventSystem { public: // Only override if the base class method is virtual! MOCK_METHOD(void, emit, (LogEvent event, const std::any& data), ()); - // subscribe and unsubscribe in LogEventSystem return EventId and bool, not void, and are not virtual - MOCK_METHOD(EventId, subscribe, (LogEvent event, EventCallback callback), ()); + // subscribe and unsubscribe in LogEventSystem return EventId and bool, not + // void, and are not virtual + MOCK_METHOD(EventId, subscribe, (LogEvent event, EventCallback callback), + ()); MOCK_METHOD(bool, unsubscribe, (LogEvent event, EventId event_id), ()); }; class MockLogFilter : public LogFilter { public: // Only override if the base class method is virtual! - MOCK_METHOD(bool, should_log, (const std::string& message, Level level, const LogContext& context), (const)); + MOCK_METHOD(bool, should_log, + (const std::string& message, Level level, + const LogContext& context), + (const)); MOCK_METHOD(void, add_filter, (FilterFunc filter), ()); MOCK_METHOD(void, clear_filters, (), ()); }; @@ -39,7 +44,8 @@ class MockLogSampler : public LogSampler { MOCK_METHOD(bool, should_sample, (), ()); MOCK_METHOD(size_t, get_dropped_count, (), (const)); MOCK_METHOD(double, get_current_rate, (), (const)); - MOCK_METHOD(void, set_strategy, (SamplingStrategy strategy, double rate), ()); + MOCK_METHOD(void, set_strategy, (SamplingStrategy strategy, double rate), + ()); MOCK_METHOD(void, reset_stats, (), ()); }; @@ -48,7 +54,8 @@ class LoggerTest : public ::testing::Test { void SetUp() override { // Create an in-memory spdlog logger for testing log_stream = std::make_shared(); - auto sink = std::make_shared(*log_stream); + auto sink = + std::make_shared(*log_stream); spdlog_logger = std::make_shared("test_logger", sink); spdlog_logger->set_level(spdlog::level::trace); @@ -61,9 +68,7 @@ class LoggerTest : public ::testing::Test { std::unique_ptr mock_event_system; MockLogEventSystem* event_system_ptr; - std::string getLogOutput() const { - return log_stream->str(); - } + std::string getLogOutput() const { return log_stream->str(); } void clearLogOutput() { log_stream->str(""); @@ -107,7 +112,8 @@ TEST_F(LoggerTest, FormattedLogging) { logger.info("User {} logged in with status {}", "john", 200); std::string output = getLogOutput(); - EXPECT_NE(output.find("User john logged in with status 200"), std::string::npos); + EXPECT_NE(output.find("User john logged in with status 200"), + std::string::npos); } TEST_F(LoggerTest, ContextEnrichment) { @@ -116,9 +122,9 @@ TEST_F(LoggerTest, ContextEnrichment) { LogContext ctx; // FIX: Use chainable with_* methods instead of set_* methods ctx.with_user("user123") - .with_session("session456") - .with_trace("trace789") - .with_request("req000"); + .with_session("session456") + .with_trace("trace789") + .with_request("req000"); logger.with_context(ctx); logger.info("test message"); @@ -340,11 +346,10 @@ TEST_F(LoggerTest, ContextMerging) { Logger logger(spdlog_logger); LogContext ctx1; - ctx1.with_user("user1") - .with_session("session1"); + ctx1.with_user("user1").with_session("session1"); LogContext ctx2; - ctx2.with_user("user2") // Should override + ctx2.with_user("user2") // Should override .with_trace("trace1"); // Should add logger.with_context(ctx1).with_context(ctx2); @@ -353,7 +358,7 @@ TEST_F(LoggerTest, ContextMerging) { std::string output = getLogOutput(); EXPECT_NE(output.find("user=user2"), std::string::npos); // Overridden EXPECT_NE(output.find("session=session1"), std::string::npos); // Preserved - EXPECT_NE(output.find("trace=trace1"), std::string::npos); // Added + EXPECT_NE(output.find("trace=trace1"), std::string::npos); // Added } TEST_F(LoggerTest, ContextualLogging) { diff --git a/atom/extra/spdlog/logger/test_manager.cpp b/atom/extra/spdlog/logger/test_manager.cpp index 78d7ea34..2c406a7c 100644 --- a/atom/extra/spdlog/logger/test_manager.cpp +++ b/atom/extra/spdlog/logger/test_manager.cpp @@ -8,7 +8,6 @@ #include "../core/types.h" #include "manager.h" - using namespace modern_log; using ::testing::_; using ::testing::Return; diff --git a/atom/extra/spdlog/sampling/sampler.cpp b/atom/extra/spdlog/sampling/sampler.cpp index 149ed2aa..79eaca76 100644 --- a/atom/extra/spdlog/sampling/sampler.cpp +++ b/atom/extra/spdlog/sampling/sampler.cpp @@ -97,8 +97,7 @@ bool LogSampler::burst_sample() { last_burst = now; } - size_t max_burst = - static_cast(sample_rate_ * 10); + size_t max_burst = static_cast(sample_rate_ * 10); bool should_log = burst_counter++ < max_burst; if (!should_log) { diff --git a/atom/extra/spdlog/utils/test_structured_data.cpp b/atom/extra/spdlog/utils/test_structured_data.cpp index 8f82d66f..94c89411 100644 --- a/atom/extra/spdlog/utils/test_structured_data.cpp +++ b/atom/extra/spdlog/utils/test_structured_data.cpp @@ -5,7 +5,6 @@ #include #include "structured_data.h" - using modern_log::StructuredData; TEST(StructuredDataTest, AddAndGetSingleField) { diff --git a/atom/extra/spdlog/utils/test_timer.cpp b/atom/extra/spdlog/utils/test_timer.cpp index 57352b5a..b8432e2b 100644 --- a/atom/extra/spdlog/utils/test_timer.cpp +++ b/atom/extra/spdlog/utils/test_timer.cpp @@ -7,7 +7,6 @@ #include "../logger/logger.h" #include "timer.h" - // Minimal mock Logger for capturing log_internal calls class MockLogger : public modern_log::Logger { public: diff --git a/atom/extra/spdlog/utils/timer.cpp b/atom/extra/spdlog/utils/timer.cpp index 14bd080d..e8889b19 100644 --- a/atom/extra/spdlog/utils/timer.cpp +++ b/atom/extra/spdlog/utils/timer.cpp @@ -4,7 +4,6 @@ #include #include "../logger/logger.h" - namespace modern_log { ScopedTimer::ScopedTimer(Logger* logger, std::string name, Level level) diff --git a/atom/extra/spdlog/utils/timer.h b/atom/extra/spdlog/utils/timer.h index da60086f..137a5570 100644 --- a/atom/extra/spdlog/utils/timer.h +++ b/atom/extra/spdlog/utils/timer.h @@ -23,10 +23,10 @@ class ScopedTimer { private: Logger* logger_; ///< Pointer to the logger instance. std::string name_; ///< Name or label for the timed scope. - Level level_; ///< Log level for reporting the timing result. - bool enabled_; ///< Whether the timer is currently enabled. + Level level_; ///< Log level for reporting the timing result. + bool enabled_; ///< Whether the timer is currently enabled. std::chrono::high_resolution_clock::time_point - start_; ///< Start time point. + start_; ///< Start time point. public: /** diff --git a/atom/extra/spdlog/xmake.lua b/atom/extra/spdlog/xmake.lua index c5dd79c6..29b7b90d 100644 --- a/atom/extra/spdlog/xmake.lua +++ b/atom/extra/spdlog/xmake.lua @@ -34,25 +34,25 @@ local headers = { target("atom-extra-spdlog") set_kind("static") - + -- Add files for _, src in ipairs(sources) do add_files(src) end - + for _, hdr in ipairs(headers) do add_headerfiles(hdr) end - + -- Include directories add_includedirs(".", {public = true}) - + -- Add packages add_packages("spdlog", "fmt") - + -- Set C++ standard set_languages("c++23") - + -- Installation on_install(function (target) local installdir = target:installdir() or "$(prefix)" @@ -63,4 +63,3 @@ target("atom-extra-spdlog") os.cp(hdr, headerdir) end end) - diff --git a/atom/extra/uv/CMakeLists.txt b/atom/extra/uv/CMakeLists.txt index 139cfb49..4ff0f680 100644 --- a/atom/extra/uv/CMakeLists.txt +++ b/atom/extra/uv/CMakeLists.txt @@ -1,18 +1,10 @@ -# CMakeLists.txt for libuv extensions -# Integrated with main Atom build system +# CMakeLists.txt for libuv extensions Integrated with main Atom build system # Source files -set(UV_SOURCES - message_bus.cpp - subprocess.cpp -) +set(UV_SOURCES message_bus.cpp subprocess.cpp) # Header files -set(UV_HEADERS - coro.hpp - message_bus.hpp - subprocess.hpp -) +set(UV_HEADERS coro.hpp message_bus.hpp subprocess.hpp) # Create library add_library(atom-extra-uv ${UV_SOURCES} ${UV_HEADERS}) @@ -21,11 +13,11 @@ add_library(atom-extra-uv ${UV_SOURCES} ${UV_HEADERS}) target_include_directories(atom-extra-uv PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) # Set target properties for consistency (C++23 required for std::expected) -set_target_properties(atom-extra-uv PROPERTIES - CXX_STANDARD 23 - CXX_STANDARD_REQUIRED ON - CXX_EXTENSIONS OFF -) +set_target_properties( + atom-extra-uv + PROPERTIES CXX_STANDARD 23 + CXX_STANDARD_REQUIRED ON + CXX_EXTENSIONS OFF) # Find and link required libraries find_package(Threads REQUIRED) @@ -34,32 +26,31 @@ target_link_libraries(atom-extra-uv PUBLIC Threads::Threads) # Find and link libuv library find_package(PkgConfig QUIET) if(PkgConfig_FOUND) - pkg_check_modules(LIBUV QUIET libuv) + pkg_check_modules(LIBUV QUIET libuv) endif() if(LIBUV_FOUND) - target_include_directories(atom-extra-uv PUBLIC ${LIBUV_INCLUDE_DIRS}) - target_link_libraries(atom-extra-uv PUBLIC ${LIBUV_LIBRARIES}) - target_compile_options(atom-extra-uv PUBLIC ${LIBUV_CFLAGS_OTHER}) - message(STATUS "Found libuv, linking to atom-extra-uv") + target_include_directories(atom-extra-uv PUBLIC ${LIBUV_INCLUDE_DIRS}) + target_link_libraries(atom-extra-uv PUBLIC ${LIBUV_LIBRARIES}) + target_compile_options(atom-extra-uv PUBLIC ${LIBUV_CFLAGS_OTHER}) + message(STATUS "Found libuv, linking to atom-extra-uv") else() - # Try to find libuv using find_library as fallback - find_library(LIBUV_LIBRARY NAMES uv libuv) - find_path(LIBUV_INCLUDE_DIR NAMES uv.h) - - if(LIBUV_LIBRARY AND LIBUV_INCLUDE_DIR) - target_include_directories(atom-extra-uv PUBLIC ${LIBUV_INCLUDE_DIR}) - target_link_libraries(atom-extra-uv PUBLIC ${LIBUV_LIBRARY}) - message(STATUS "Found libuv library: ${LIBUV_LIBRARY}") - else() - message(WARNING "libuv not found - atom-extra-uv may have linking issues") - message(STATUS "Please install libuv development package:") - message(STATUS " Ubuntu/Debian: sudo apt-get install libuv1-dev") - message(STATUS " CentOS/RHEL: sudo yum install libuv-devel") - message(STATUS " Windows: vcpkg install libuv") - endif() + # Try to find libuv using find_library as fallback + find_library(LIBUV_LIBRARY NAMES uv libuv) + find_path(LIBUV_INCLUDE_DIR NAMES uv.h) + + if(LIBUV_LIBRARY AND LIBUV_INCLUDE_DIR) + target_include_directories(atom-extra-uv PUBLIC ${LIBUV_INCLUDE_DIR}) + target_link_libraries(atom-extra-uv PUBLIC ${LIBUV_LIBRARY}) + message(STATUS "Found libuv library: ${LIBUV_LIBRARY}") + else() + message(WARNING "libuv not found - atom-extra-uv may have linking issues") + message(STATUS "Please install libuv development package:") + message(STATUS " Ubuntu/Debian: sudo apt-get install libuv1-dev") + message(STATUS " CentOS/RHEL: sudo yum install libuv-devel") + message(STATUS " Windows: vcpkg install libuv") + endif() endif() -# Installation rules (optional) -# install(TARGETS atom-extra-uv DESTINATION lib) +# Installation rules (optional) install(TARGETS atom-extra-uv DESTINATION lib) # install(FILES ${UV_HEADERS} DESTINATION include/atom/extra/uv) diff --git a/atom/extra/uv/coro.hpp b/atom/extra/uv/coro.hpp index e901b50a..c8e64354 100644 --- a/atom/extra/uv/coro.hpp +++ b/atom/extra/uv/coro.hpp @@ -7,16 +7,16 @@ #define ATOM_EXTRA_UV_CORO_HPP #include +#include #include #include #include #include #include #include -#include #ifdef _WIN32 -#include #include +#include #else #include #include @@ -304,13 +304,13 @@ class Scheduler { void run_once() { uv_run(loop_, UV_RUN_ONCE); } void stop() { uv_stop(loop_); } - - template + + template void schedule(Task task) { // Start the task - this will begin execution // Since our Task uses suspend_never for initial_suspend, // the task will start immediately - (void)task; // Task will run to completion or suspend + (void)task; // Task will run to completion or suspend } private: @@ -1060,9 +1060,11 @@ inline TimeoutAwaiter timeout(uint64_t timeout_ms) { return TimeoutAwaiter(get_scheduler().get_loop(), timeout_ms); } -template -inline TimeoutAwaiter timeout(const std::chrono::duration& timeout) { - auto ms = std::chrono::duration_cast(timeout).count(); +template +inline TimeoutAwaiter timeout( + const std::chrono::duration& timeout) { + auto ms = + std::chrono::duration_cast(timeout).count(); return TimeoutAwaiter(get_scheduler().get_loop(), ms); } @@ -1070,9 +1072,7 @@ inline TcpConnectAwaiter tcp_connect(const std::string& host, int port) { return TcpConnectAwaiter(get_scheduler().get_loop(), host, port); } -inline TcpReadAwaiter tcp_read(uv_tcp_t* tcp) { - return TcpReadAwaiter(tcp); -} +inline TcpReadAwaiter tcp_read(uv_tcp_t* tcp) { return TcpReadAwaiter(tcp); } inline TcpWriteAwaiter tcp_write(uv_tcp_t* tcp, const std::string& data) { return TcpWriteAwaiter(tcp, data); @@ -1109,9 +1109,9 @@ class UdpSendPlaceholder { public: bool await_ready() const { return true; } void await_suspend(std::coroutine_handle<>) {} - UdpSendResult await_resume() { + UdpSendResult await_resume() { // Placeholder - always succeeds - return UdpSendResult{10}; + return UdpSendResult{10}; } }; @@ -1119,22 +1119,27 @@ class UdpReceivePlaceholder { public: bool await_ready() const { return true; } void await_suspend(std::coroutine_handle<>) {} - UdpReceiveResult await_resume() { + UdpReceiveResult await_resume() { // Placeholder - throw timeout to simulate no server throw UvError(UV_ETIMEDOUT); } }; -inline UdpSendPlaceholder udp_send(const std::string& host, int port, const std::string& message) { +inline UdpSendPlaceholder udp_send(const std::string& host, int port, + const std::string& message) { // Placeholder implementation - (void)host; (void)port; (void)message; + (void)host; + (void)port; + (void)message; return UdpSendPlaceholder{}; } -template -inline UdpReceivePlaceholder udp_receive(int port, const std::chrono::duration& timeout) { +template +inline UdpReceivePlaceholder udp_receive( + int port, const std::chrono::duration& timeout) { // Placeholder implementation - (void)port; (void)timeout; + (void)port; + (void)timeout; return UdpReceivePlaceholder{}; } @@ -1153,14 +1158,14 @@ struct ProcessResult { class ProcessPlaceholder { public: ProcessPlaceholder(const ProcessOptions& opts) : options(opts) {} - + bool await_ready() const { return true; } void await_suspend(std::coroutine_handle<>) {} - ProcessResult await_resume() { + ProcessResult await_resume() { // Placeholder - always succeeds with fake output return ProcessResult{0, "Hello from subprocess!", ""}; } - + private: ProcessOptions options; }; diff --git a/atom/extra/uv/message_bus.cpp b/atom/extra/uv/message_bus.cpp index f53d2ebc..016d3ba2 100644 --- a/atom/extra/uv/message_bus.cpp +++ b/atom/extra/uv/message_bus.cpp @@ -16,13 +16,13 @@ struct MessageBus::Impl { using HandlerMap = std::unordered_map>; using TopicHandlers = std::unordered_map; using TypeHandlers = std::unordered_map; - + mutable std::shared_mutex handlers_mutex; TypeHandlers handlers; - + mutable std::mutex message_queue_mutex; std::queue> message_queue; - + std::atomic avg_delivery_time{std::chrono::milliseconds(0)}; }; @@ -32,8 +32,8 @@ MessageBus::MessageBus(const BackPressureConfig& config) spdlog::info("MessageBus initialized with max queue size: {}", config_.max_queue_size); } -MessageBus::~MessageBus() { - shutdown(); +MessageBus::~MessageBus() { + shutdown(); } void MessageBus::shutdown() { @@ -98,4 +98,4 @@ MessageBus::QueueStats MessageBus::get_stats() const { } -} // namespace msgbus \ No newline at end of file +} // namespace msgbus diff --git a/atom/extra/uv/message_bus.hpp b/atom/extra/uv/message_bus.hpp index 133914d3..8166592b 100644 --- a/atom/extra/uv/message_bus.hpp +++ b/atom/extra/uv/message_bus.hpp @@ -6,22 +6,22 @@ #include // Temporarily disable std::expected usage until compiler support is stable // #include -#include +#include #include #include #include +#include +#include #include #include -#include -#include -#include +#include #include namespace msgbus { // Simple Result type as a replacement for std::expected -template +template class Result { private: std::variant data_; @@ -46,7 +46,7 @@ class Result { }; // Specialization for void type -template +template class Result { private: std::optional error_; @@ -163,7 +163,8 @@ struct MessageAwaiter { Result, MessageBusError> await_resume(); private: - std::shared_ptr, MessageBusError>>> promise_; + std::shared_ptr, MessageBusError>>> + promise_; }; // **MessageBus Class Declaration** @@ -182,8 +183,8 @@ class MessageBus { // Template-based subscription template Handler> SubscriptionHandle subscribe(const std::string& topic_pattern, - Handler&& handler, - MessageFilter filter = nullptr); + Handler&& handler, + MessageFilter filter = nullptr); // Publish message template @@ -205,9 +206,9 @@ class MessageBus { // Get queue statistics QueueStats get_stats() const; - + void shutdown(); - void process_messages(); // Synchronous processing for examples + void process_messages(); // Synchronous processing for examples static MessageBus* get_instance(); @@ -224,13 +225,15 @@ class MessageBus { // Template method implementations template Handler> SubscriptionHandle MessageBus::subscribe(const std::string& topic_pattern, - Handler&& handler, - MessageFilter filter) { + Handler&& handler, + MessageFilter filter) { uint64_t handler_id = handler_id_counter_++; - auto wrapper = [handler = std::forward(handler), filter](const std::any& envelope_any) { + auto wrapper = [handler = std::forward(handler), + filter](const std::any& envelope_any) { try { - const auto& envelope = std::any_cast&>(envelope_any); + const auto& envelope = + std::any_cast&>(envelope_any); if (!filter || filter(envelope)) { handler(envelope.payload); } @@ -245,12 +248,13 @@ SubscriptionHandle MessageBus::subscribe(const std::string& topic_pattern, // Basic cleanup }; - return std::make_unique(handler_id, topic_pattern, cleanup); + return std::make_unique(handler_id, topic_pattern, + cleanup); } template -Result MessageBus::publish(const std::string& topic, T&& message, - const std::string& sender_id) { +Result MessageBus::publish( + const std::string& topic, T&& message, const std::string& sender_id) { if (shutdown_.load()) { return MessageBusError::ShutdownInProgress; } @@ -260,7 +264,7 @@ Result MessageBus::publish(const std::string& topic, T&& // For this basic implementation, just return success // In a full implementation, this would queue the message for processing - return {}; // Success + return {}; // Success } catch (const std::exception& e) { return MessageBusError::SerializationError; } diff --git a/atom/extra/uv/xmake.lua b/atom/extra/uv/xmake.lua index 0ff02247..dff6fd86 100644 --- a/atom/extra/uv/xmake.lua +++ b/atom/extra/uv/xmake.lua @@ -27,28 +27,28 @@ local headers = { target("atom-extra-uv") set_kind("static") - + -- Add files for _, src in ipairs(sources) do add_files(src) end - + for _, hdr in ipairs(headers) do add_headerfiles(hdr) end - + -- Include directories add_includedirs(".", {public = true}) - + -- Add packages add_packages("libuv") - + -- Add system libraries add_syslinks("pthread") - + -- Set C++ standard set_languages("c++23") - + -- Installation on_install(function (target) local installdir = target:installdir() or "$(prefix)" @@ -59,4 +59,3 @@ target("atom-extra-uv") os.cp(hdr, headerdir) end end) - diff --git a/atom/extra/xmake.lua b/atom/extra/xmake.lua index c4c812be..3ac35f98 100644 --- a/atom/extra/xmake.lua +++ b/atom/extra/xmake.lua @@ -64,7 +64,7 @@ end if has_config("unified_extra") then target("atom-extra-unified") set_kind("headeronly") - + -- Collect all extra targets local extra_targets = { "atom-extra-asio", @@ -79,15 +79,15 @@ if has_config("unified_extra") then "atom-extra-spdlog", "atom-extra-uv" } - + -- Add dependencies on all extra components for _, target_name in ipairs(extra_targets) do add_deps(target_name, {public = true}) end - + -- Set C++ standard set_languages("c++20") - + -- Installation on_install(function (target) print("Unified extra library installed") @@ -99,4 +99,3 @@ end after_load(function () print("Atom Extra Components configuration completed") end) - diff --git a/atom/image/CMakeLists.txt b/atom/image/CMakeLists.txt index c19de862..52dfaabe 100644 --- a/atom/image/CMakeLists.txt +++ b/atom/image/CMakeLists.txt @@ -1,5 +1,8 @@ cmake_minimum_required(VERSION 3.21) -project(atom-image VERSION 1.0.0 LANGUAGES CXX) +project( + atom-image + VERSION 1.0.0 + LANGUAGES CXX) # Include standardized module configuration include(${CMAKE_SOURCE_DIR}/cmake/ModuleDependencies.cmake) @@ -8,19 +11,18 @@ include(${CMAKE_SOURCE_DIR}/cmake/ModuleDependencies.cmake) find_package(loguru QUIET) # Find optional packages -find_package(OpenCV REQUIRED) # Change to REQUIRED for computer vision +find_package(OpenCV REQUIRED) # Change to REQUIRED for computer vision find_package(PkgConfig QUIET) if(PkgConfig_FOUND) - pkg_check_modules(CFITSIO cfitsio) - pkg_check_modules(TESSERACT tesseract) - pkg_check_modules(LEPTONICA lept) + pkg_check_modules(CFITSIO cfitsio) + pkg_check_modules(TESSERACT tesseract) + pkg_check_modules(LEPTONICA lept) endif() # Define source files set(ATOM_IMAGE_SOURCES # Metadata sources metadata/exif.cpp - # Format sources formats/fits_data.cpp formats/fits_file.cpp @@ -28,34 +30,28 @@ set(ATOM_IMAGE_SOURCES formats/fits_utils.cpp formats/hdu.cpp formats/advanced_formats.cpp - # I/O sources io/format_detector.cpp io/image_loader.cpp io/image_saver.cpp - # Processing sources processing/image_processor.cpp processing/filters.cpp processing/transforms.cpp processing/enhancement.cpp - processing/computer_vision.cpp # Uncommented + processing/computer_vision.cpp # Uncommented processing/ml_processing.cpp processing/gpu_acceleration.cpp - processing/realtime_stub.cpp -) + processing/realtime_stub.cpp) # Define header files set(ATOM_IMAGE_HEADERS # Main header image.hpp - # Core headers core/image_blob.hpp - # Metadata headers metadata/exif.hpp - # Format headers formats/fits_data.hpp formats/fits_file.hpp @@ -63,100 +59,97 @@ set(ATOM_IMAGE_HEADERS formats/fits_utils.hpp formats/hdu.hpp formats/advanced_formats.hpp - # I/O headers io/format_detector.hpp io/image_loader.hpp io/image_saver.hpp - # Processing headers processing/image_processor.hpp processing/filters.hpp processing/transforms.hpp processing/enhancement.hpp - processing/computer_vision.hpp # Added + processing/computer_vision.hpp # Added processing/ml_processing.hpp processing/gpu_acceleration.hpp - processing/realtime.hpp -) + processing/realtime.hpp) # Add OCR sources if Tesseract is available if(TESSERACT_FOUND AND LEPTONICA_FOUND) - list(APPEND ATOM_IMAGE_SOURCES processing/ocr/ocr.cpp) - list(APPEND ATOM_IMAGE_HEADERS processing/ocr/ocr.hpp) - set(ATOM_IMAGE_HAS_OCR ON) + list(APPEND ATOM_IMAGE_SOURCES processing/ocr/ocr.cpp) + list(APPEND ATOM_IMAGE_HEADERS processing/ocr/ocr.hpp) + set(ATOM_IMAGE_HAS_OCR ON) endif() # Add SER sources (requires OpenCV) if(OpenCV_FOUND) - list(APPEND ATOM_IMAGE_SOURCES - formats/ser/ser_reader.cpp - formats/ser/ser_writer.cpp - formats/ser/frame_processor.cpp - formats/ser/quality.cpp - formats/ser/utils.cpp - ) - - list(APPEND ATOM_IMAGE_HEADERS - formats/ser/ser.hpp - formats/ser/ser_reader.h - formats/ser/ser_writer.h - formats/ser/frame_processor.h - formats/ser/quality.h - formats/ser/registration.h - formats/ser/ser_format.h - formats/ser/stacking.h - formats/ser/utils.h - formats/ser/exception.h - ) + list( + APPEND + ATOM_IMAGE_SOURCES + formats/ser/ser_reader.cpp + formats/ser/ser_writer.cpp + formats/ser/frame_processor.cpp + formats/ser/quality.cpp + formats/ser/utils.cpp) + + list( + APPEND + ATOM_IMAGE_HEADERS + formats/ser/ser.hpp + formats/ser/ser_reader.h + formats/ser/ser_writer.h + formats/ser/frame_processor.h + formats/ser/quality.h + formats/ser/registration.h + formats/ser/ser_format.h + formats/ser/stacking.h + formats/ser/utils.h + formats/ser/exception.h) endif() # Create object library add_library(atom-image-object OBJECT ${ATOM_IMAGE_SOURCES}) # Set include directories -target_include_directories(atom-image-object - PUBLIC - $ - $ - $ -) +target_include_directories( + atom-image-object + PUBLIC $ + $ + $) # Link required libraries set(ATOM_IMAGE_LINK_LIBRARIES) if(TARGET atom-error) - list(APPEND ATOM_IMAGE_LINK_LIBRARIES atom-error) + list(APPEND ATOM_IMAGE_LINK_LIBRARIES atom-error) endif() if(loguru_FOUND AND TARGET loguru::loguru) - list(APPEND ATOM_IMAGE_LINK_LIBRARIES loguru::loguru) + list(APPEND ATOM_IMAGE_LINK_LIBRARIES loguru::loguru) endif() if(ATOM_IMAGE_LINK_LIBRARIES) - target_link_libraries(atom-image-object - PUBLIC - ${ATOM_IMAGE_LINK_LIBRARIES} - ) + target_link_libraries(atom-image-object PUBLIC ${ATOM_IMAGE_LINK_LIBRARIES}) endif() # Link optional libraries if(OpenCV_FOUND) - target_link_libraries(atom-image-object PUBLIC ${OpenCV_LIBS}) - target_include_directories(atom-image-object PUBLIC ${OpenCV_INCLUDE_DIRS}) - target_compile_definitions(atom-image-object PUBLIC ATOM_IMAGE_HAS_OPENCV) + target_link_libraries(atom-image-object PUBLIC ${OpenCV_LIBS}) + target_include_directories(atom-image-object PUBLIC ${OpenCV_INCLUDE_DIRS}) + target_compile_definitions(atom-image-object PUBLIC ATOM_IMAGE_HAS_OPENCV) endif() if(CFITSIO_FOUND) - target_link_libraries(atom-image-object PUBLIC ${CFITSIO_LIBRARIES}) - target_include_directories(atom-image-object PUBLIC ${CFITSIO_INCLUDE_DIRS}) - target_compile_definitions(atom-image-object PUBLIC ATOM_IMAGE_HAS_CFITSIO) + target_link_libraries(atom-image-object PUBLIC ${CFITSIO_LIBRARIES}) + target_include_directories(atom-image-object PUBLIC ${CFITSIO_INCLUDE_DIRS}) + target_compile_definitions(atom-image-object PUBLIC ATOM_IMAGE_HAS_CFITSIO) endif() if(ATOM_IMAGE_HAS_OCR) - target_link_libraries(atom-image-object PUBLIC ${TESSERACT_LIBRARIES} ${LEPTONICA_LIBRARIES}) - target_include_directories(atom-image-object PUBLIC ${TESSERACT_INCLUDE_DIRS} ${LEPTONICA_INCLUDE_DIRS}) - target_compile_definitions(atom-image-object PUBLIC ATOM_IMAGE_HAS_OCR) + target_link_libraries(atom-image-object PUBLIC ${TESSERACT_LIBRARIES} + ${LEPTONICA_LIBRARIES}) + target_include_directories(atom-image-object PUBLIC ${TESSERACT_INCLUDE_DIRS} + ${LEPTONICA_INCLUDE_DIRS}) + target_compile_definitions(atom-image-object PUBLIC ATOM_IMAGE_HAS_OCR) endif() # Set compiler flags @@ -170,26 +163,28 @@ atom_configure_module(atom-image) # Link module-specific dependencies if(ATOM_IMAGE_LINK_LIBRARIES) - target_link_libraries(atom-image PUBLIC ${ATOM_IMAGE_LINK_LIBRARIES}) + target_link_libraries(atom-image PUBLIC ${ATOM_IMAGE_LINK_LIBRARIES}) endif() # Link optional dependencies if(OpenCV_FOUND) - target_link_libraries(atom-image PUBLIC ${OpenCV_LIBS}) - target_include_directories(atom-image PUBLIC ${OpenCV_INCLUDE_DIRS}) - target_compile_definitions(atom-image PUBLIC ATOM_IMAGE_HAS_OPENCV) + target_link_libraries(atom-image PUBLIC ${OpenCV_LIBS}) + target_include_directories(atom-image PUBLIC ${OpenCV_INCLUDE_DIRS}) + target_compile_definitions(atom-image PUBLIC ATOM_IMAGE_HAS_OPENCV) endif() if(CFITSIO_FOUND) - target_link_libraries(atom-image PUBLIC ${CFITSIO_LIBRARIES}) - target_include_directories(atom-image PUBLIC ${CFITSIO_INCLUDE_DIRS}) - target_compile_definitions(atom-image PUBLIC ATOM_IMAGE_HAS_CFITSIO) + target_link_libraries(atom-image PUBLIC ${CFITSIO_LIBRARIES}) + target_include_directories(atom-image PUBLIC ${CFITSIO_INCLUDE_DIRS}) + target_compile_definitions(atom-image PUBLIC ATOM_IMAGE_HAS_CFITSIO) endif() if(ATOM_IMAGE_HAS_OCR) - target_link_libraries(atom-image PUBLIC ${TESSERACT_LIBRARIES} ${LEPTONICA_LIBRARIES}) - target_include_directories(atom-image PUBLIC ${TESSERACT_INCLUDE_DIRS} ${LEPTONICA_INCLUDE_DIRS}) - target_compile_definitions(atom-image PUBLIC ATOM_IMAGE_HAS_OCR) + target_link_libraries(atom-image PUBLIC ${TESSERACT_LIBRARIES} + ${LEPTONICA_LIBRARIES}) + target_include_directories(atom-image PUBLIC ${TESSERACT_INCLUDE_DIRS} + ${LEPTONICA_INCLUDE_DIRS}) + target_compile_definitions(atom-image PUBLIC ATOM_IMAGE_HAS_OCR) endif() # Set compiler flags @@ -197,32 +192,24 @@ target_compile_features(atom-image PUBLIC cxx_std_20) # Install headers install(FILES ${ATOM_IMAGE_HEADERS} - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/atom/image -) + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/atom/image) -# Note: Export disabled due to dependency issues -# install(EXPORT atom-image-targets -# FILE atom-image-targets.cmake -# NAMESPACE atom:: -# DESTINATION lib/cmake/atom-image -# ) +# Note: Export disabled due to dependency issues install(EXPORT +# atom-image-targets FILE atom-image-targets.cmake NAMESPACE atom:: DESTINATION +# lib/cmake/atom-image ) # Create config file include(CMakePackageConfigHelpers) write_basic_package_version_file( - atom-image-config-version.cmake - VERSION ${PROJECT_VERSION} - COMPATIBILITY SameMajorVersion -) + atom-image-config-version.cmake + VERSION ${PROJECT_VERSION} + COMPATIBILITY SameMajorVersion) configure_package_config_file( - ${CMAKE_CURRENT_SOURCE_DIR}/atom-image-config.cmake.in - ${CMAKE_CURRENT_BINARY_DIR}/atom-image-config.cmake - INSTALL_DESTINATION lib/cmake/atom-image -) - -install(FILES - ${CMAKE_CURRENT_BINARY_DIR}/atom-image-config.cmake - ${CMAKE_CURRENT_BINARY_DIR}/atom-image-config-version.cmake - DESTINATION lib/cmake/atom-image -) + ${CMAKE_CURRENT_SOURCE_DIR}/atom-image-config.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/atom-image-config.cmake + INSTALL_DESTINATION lib/cmake/atom-image) + +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/atom-image-config.cmake + ${CMAKE_CURRENT_BINARY_DIR}/atom-image-config-version.cmake + DESTINATION lib/cmake/atom-image) diff --git a/atom/image/PERFORMANCE.md b/atom/image/PERFORMANCE.md index dc79a316..3b85d9ec 100644 --- a/atom/image/PERFORMANCE.md +++ b/atom/image/PERFORMANCE.md @@ -21,12 +21,14 @@ atom::image::fast_blob fast_blob(data, size); ``` **Use fast_blob when:** + - Processing large datasets where memory copying is expensive - Implementing view-only operations - Working with memory-mapped files - Interfacing with external libraries that manage memory **Use normal blob when:** + - You need to modify image dimensions - Performing operations that change image size - Building processing pipelines with multiple transformations diff --git a/atom/image/README.md b/atom/image/README.md index 59bae16e..98b865e9 100644 --- a/atom/image/README.md +++ b/atom/image/README.md @@ -224,6 +224,7 @@ xmake test ### Core Classes #### `atom::image::blob` + Universal image container with multiple backend support. ```cpp @@ -250,6 +251,7 @@ blob restored = blob::deserialize(serialized); ``` #### `atom::image::ImageProcessor` + High-performance image processing pipeline. ```cpp @@ -277,6 +279,7 @@ auto results = processor->processBatch(inputs, [&](const blob& img) { ``` #### `atom::image::FITSFile` + Astronomical image format support. ```cpp @@ -299,6 +302,7 @@ fits.writeFITS("processed_data.fits"); ``` #### `atom::image::EnhancedOCRProcessor` + Optical character recognition with preprocessing. ```cpp @@ -320,6 +324,7 @@ auto results = ocr.processBatch(images); ``` #### `atom::image::SERReader` / `atom::image::SERWriter` + Astronomical video sequence format. ```cpp @@ -366,6 +371,7 @@ try { ### Performance Optimization #### Memory Management + ```cpp // Use fast blobs for view-only operations fast_blob view(data_ptr, size); @@ -383,6 +389,7 @@ processor->setOptions(options); ``` #### Batch Processing + ```cpp // Process multiple images efficiently std::vector images = loadImages(); @@ -429,6 +436,7 @@ make coverage ### Common Issues #### OpenCV Not Found + ```bash # Install OpenCV development packages sudo apt-get install libopencv-dev # Ubuntu/Debian @@ -436,6 +444,7 @@ brew install opencv # macOS ``` #### CFITSIO Not Found + ```bash # Install CFITSIO development packages sudo apt-get install libcfitsio-dev # Ubuntu/Debian @@ -443,6 +452,7 @@ brew install cfitsio # macOS ``` #### Tesseract Not Found + ```bash # Install Tesseract and Leptonica sudo apt-get install tesseract-ocr-dev libleptonica-dev # Ubuntu/Debian @@ -450,11 +460,13 @@ brew install tesseract leptonica # macOS ``` #### Memory Issues + - Reduce `maxMemoryUsage` in ProcessingOptions - Use `fast_blob` for large datasets - Enable memory pooling for frequent operations #### Performance Issues + - Enable SIMD optimizations - Use multithreading for batch operations - Consider GPU acceleration for large images diff --git a/atom/image/build_minimal/build.ninja b/atom/image/build_minimal/build.ninja index b817fadd..5c4ee522 100644 --- a/atom/image/build_minimal/build.ninja +++ b/atom/image/build_minimal/build.ninja @@ -6,13 +6,13 @@ # ============================================================================= # Write statements declared in CMakeLists.txt: -# +# # Which is the root file. # ============================================================================= # ============================================================================= # Project: atom-image-minimal -# Configurations: +# Configurations: # ============================================================================= ############################################# diff --git a/atom/image/core/README.md b/atom/image/core/README.md index 78dbe167..8c8cdd5a 100644 --- a/atom/image/core/README.md +++ b/atom/image/core/README.md @@ -147,10 +147,12 @@ try { ## Dependencies ### Required + - atom-error: Error handling framework - Standard C++20 library ### Optional + - OpenCV: Advanced image operations - CImg: Alternative image processing backend - stb_image: Lightweight image I/O diff --git a/atom/image/core/cache_manager.hpp b/atom/image/core/cache_manager.hpp index 4577d5e3..d9af6d9a 100644 --- a/atom/image/core/cache_manager.hpp +++ b/atom/image/core/cache_manager.hpp @@ -300,4 +300,4 @@ void CacheManager::updateAccessOrder(const std::string& key) { // This is a placeholder for LRU-based access tracking } -} // namespace atom::image::core \ No newline at end of file +} // namespace atom::image::core diff --git a/atom/image/core/exceptions.hpp b/atom/image/core/exceptions.hpp index e677a7d6..c4a4410f 100644 --- a/atom/image/core/exceptions.hpp +++ b/atom/image/core/exceptions.hpp @@ -132,4 +132,4 @@ class ConfigurationException : public ImageException { : ImageException("Configuration error: " + message) {} }; -} // namespace atom::image::core \ No newline at end of file +} // namespace atom::image::core diff --git a/atom/image/core/image_blob.hpp b/atom/image/core/image_blob.hpp index 20c65e96..225c2a96 100644 --- a/atom/image/core/image_blob.hpp +++ b/atom/image/core/image_blob.hpp @@ -3,15 +3,14 @@ #include #include +#include #include #include #include -#include -#include -#include #include +#include #include - +#include // Forward declare error macros - actual definitions in source files #ifndef THROW_RUNTIME_ERROR @@ -91,7 +90,8 @@ class Blob { Blob(std::nullptr_t, size_t n) { if (n != 0) { - THROW_RUNTIME_ERROR("Cannot create Blob from null pointer with non-zero size"); + THROW_RUNTIME_ERROR( + "Cannot create Blob from null pointer with non-zero size"); } if constexpr (Mode == BlobMode::FAST) { storage_ = std::span(); @@ -109,10 +109,12 @@ class Blob { } Blob(void* ptr, size_t n) - requires(Mode == BlobMode::FAST) { + requires(Mode == BlobMode::FAST) + { if (ptr == nullptr) { if (n != 0) { - THROW_RUNTIME_ERROR("Cannot create Blob from null pointer with non-zero size"); + THROW_RUNTIME_ERROR( + "Cannot create Blob from null pointer with non-zero size"); } storage_ = std::span(); return; @@ -129,7 +131,8 @@ class Blob { Blob(U* ptr, size_t n) { if (ptr == nullptr) { if (n != 0) { - THROW_RUNTIME_ERROR("Cannot create Blob from null pointer with non-zero size"); + THROW_RUNTIME_ERROR( + "Cannot create Blob from null pointer with non-zero size"); } if constexpr (Mode == BlobMode::FAST) { storage_ = std::span(); @@ -143,7 +146,8 @@ class Blob { if constexpr (Mode == BlobMode::FAST) { if constexpr (std::is_const_v) { - THROW_RUNTIME_ERROR("Cannot create fast blob from const data source"); + THROW_RUNTIME_ERROR( + "Cannot create fast blob from const data source"); } auto* byte_ptr = reinterpret_cast(ptr); storage_ = std::span(byte_ptr, byte_count); @@ -156,7 +160,8 @@ class Blob { template explicit Blob(const std::array& arr) { if constexpr (Mode == BlobMode::FAST) { - storage_ = std::span(reinterpret_cast(arr.data()), N * sizeof(U)); + storage_ = std::span(reinterpret_cast(arr.data()), + N * sizeof(U)); } else { storage_.resize(N * sizeof(U)); std::memcpy(storage_.data(), arr.data(), N * sizeof(U)); @@ -164,10 +169,12 @@ class Blob { } // Constructor from raw data with dimensions - Blob(void* ptr, size_t size, int rows, int cols, int channels = 1, int depth = DEFAULT_DEPTH) { + Blob(void* ptr, size_t size, int rows, int cols, int channels = 1, + int depth = DEFAULT_DEPTH) { if (ptr == nullptr) { if (size != 0) { - THROW_RUNTIME_ERROR("Cannot create Blob from null pointer with non-zero size"); + THROW_RUNTIME_ERROR( + "Cannot create Blob from null pointer with non-zero size"); } if constexpr (Mode == BlobMode::FAST) { storage_ = std::span(); @@ -189,10 +196,12 @@ class Blob { template explicit Blob(std::array& arr) { if constexpr (Mode == BlobMode::FAST) { - storage_ = std::span(reinterpret_cast(arr.data()), sizeof(U) * N); + storage_ = + std::span(reinterpret_cast(arr.data()), sizeof(U) * N); } else { - storage_ = std::vector(reinterpret_cast(arr.data()), - reinterpret_cast(arr.data()) + sizeof(U) * N); + storage_ = std::vector( + reinterpret_cast(arr.data()), + reinterpret_cast(arr.data()) + sizeof(U) * N); } } @@ -323,17 +332,20 @@ class Blob { THROW_OUT_OF_RANGE("Slice range out of bounds"); } Blob result; - // Preserve original geometry if possible: infer rows from current row width + // Preserve original geometry if possible: infer rows from current row + // width result.channels_ = channels_; result.depth_ = depth_; if (cols_ > 0 && channels_ > 0) { - const size_t row_bytes = static_cast(cols_) * static_cast(channels_); + const size_t row_bytes = + static_cast(cols_) * static_cast(channels_); if (length % row_bytes == 0) { result.rows_ = static_cast(length / row_bytes); result.cols_ = cols_; } else { result.rows_ = 1; - result.cols_ = static_cast(length / std::max(channels_, 1)); + result.cols_ = + static_cast(length / std::max(channels_, 1)); } } else { result.rows_ = 1; @@ -373,15 +385,18 @@ class Blob { const auto* bytePtr = reinterpret_cast(ptr); storage_.insert(storage_.end(), bytePtr, bytePtr + n); // Update dimensions only when they are meaningful and consistent - size_t denom = static_cast(cols_) * static_cast(channels_); + size_t denom = + static_cast(cols_) * static_cast(channels_); if (denom > 0 && n % denom == 0) { rows_ += static_cast(n / denom); } else if (cols_ == 0 || channels_ == 0) { // Initialize a sane default to avoid division by zero later channels_ = channels_ == 0 ? 1 : channels_; cols_ = cols_ == 0 ? static_cast(n) : cols_; - // With this initialization, the current buffer represents one row - if (n == static_cast(cols_) * static_cast(channels_)) { + // With this initialization, the current buffer represents one + // row + if (n == static_cast(cols_) * + static_cast(channels_)) { rows_ = std::max(1, rows_); } } @@ -571,14 +586,16 @@ class Blob { return channels_ * (depth_ == 8 ? 1 : depth_ == 16 ? 2 : 4); } [[nodiscard]] auto getImageSize() const -> size_t { - return static_cast(rows_) * static_cast(cols_) * getPixelSize(); + return static_cast(rows_) * static_cast(cols_) * + getPixelSize(); } // Memory alignment for performance void alignMemory(size_t alignment = 64) { if constexpr (Mode == BlobMode::NORMAL) { if (storage_.size() % alignment != 0) { - size_t newSize = ((storage_.size() + alignment - 1) / alignment) * alignment; + size_t newSize = + ((storage_.size() + alignment - 1) / alignment) * alignment; storage_.resize(newSize); } } @@ -613,18 +630,21 @@ class Blob { result.depth_ = depth_; if constexpr (Mode == BlobMode::NORMAL) { - result.storage_.reserve(static_cast(width) * static_cast(height) * - static_cast(channels_)); + result.storage_.reserve(static_cast(width) * + static_cast(height) * + static_cast(channels_)); size_t pixelSize = getPixelSize(); for (int row = y; row < y + height; ++row) { - size_t srcOffset = (static_cast(row) * static_cast(cols_) + - static_cast(x)) * pixelSize; + size_t srcOffset = + (static_cast(row) * static_cast(cols_) + + static_cast(x)) * + pixelSize; size_t copySize = static_cast(width) * pixelSize; result.storage_.insert(result.storage_.end(), - storage_.begin() + srcOffset, - storage_.begin() + srcOffset + copySize); + storage_.begin() + srcOffset, + storage_.begin() + srcOffset + copySize); } } diff --git a/atom/image/core/image_metadata.hpp b/atom/image/core/image_metadata.hpp index d13029ac..fbd902cd 100644 --- a/atom/image/core/image_metadata.hpp +++ b/atom/image/core/image_metadata.hpp @@ -1,11 +1,11 @@ #pragma once +#include +#include #include #include #include -#include #include -#include namespace atom::image::core { @@ -13,15 +13,8 @@ namespace atom::image::core { * @brief Type alias for metadata values */ using MetadataValue = std::variant< - std::string, - int, - double, - bool, - std::chrono::system_clock::time_point, - std::vector, - std::vector, - std::vector ->; + std::string, int, double, bool, std::chrono::system_clock::time_point, + std::vector, std::vector, std::vector >; /** * @brief Image metadata container @@ -64,7 +57,7 @@ class ImageMetadata { * @param key Metadata key * @param value Metadata value */ - template + template void set(const std::string& key, const T& value) { metadata_[key] = value; } @@ -76,7 +69,7 @@ class ImageMetadata { * @param defaultValue Default value if key not found * @return Metadata value or default */ - template + template T get(const std::string& key, const T& defaultValue = T{}) const { auto it = metadata_.find(key); if (it != metadata_.end()) { @@ -93,7 +86,8 @@ class ImageMetadata { * @param defaultValue Default value if key not found * @return Metadata value as string */ - std::string getString(const std::string& key, const std::string& defaultValue = "") const; + std::string getString(const std::string& key, + const std::string& defaultValue = "") const; /** * @brief Get a metadata value as integer @@ -133,16 +127,12 @@ class ImageMetadata { * @param key Metadata key * @return True if entry was removed */ - bool remove(const std::string& key) { - return metadata_.erase(key) > 0; - } + bool remove(const std::string& key) { return metadata_.erase(key) > 0; } /** * @brief Clear all metadata */ - void clear() { - metadata_.clear(); - } + void clear() { metadata_.clear(); } /** * @brief Get all metadata keys @@ -154,17 +144,13 @@ class ImageMetadata { * @brief Get the number of metadata entries * @return Number of entries */ - size_t size() const { - return metadata_.size(); - } + size_t size() const { return metadata_.size(); } /** * @brief Check if metadata is empty * @return True if empty */ - bool empty() const { - return metadata_.empty(); - } + bool empty() const { return metadata_.empty(); } /** * @brief Merge metadata from another object @@ -202,56 +188,57 @@ class ImageMetadata { * @param typeHint Type hint for conversion * @return Metadata value */ - MetadataValue stringToValue(const std::string& str, const std::string& typeHint) const; + MetadataValue stringToValue(const std::string& str, + const std::string& typeHint) const; }; /** * @brief Standard metadata keys */ namespace MetadataKeys { - // Basic image information - constexpr const char* WIDTH = "width"; - constexpr const char* HEIGHT = "height"; - constexpr const char* CHANNELS = "channels"; - constexpr const char* BIT_DEPTH = "bit_depth"; - constexpr const char* COLOR_SPACE = "color_space"; - constexpr const char* FORMAT = "format"; - - // Camera information - constexpr const char* CAMERA_MAKE = "camera_make"; - constexpr const char* CAMERA_MODEL = "camera_model"; - constexpr const char* LENS_MODEL = "lens_model"; - constexpr const char* FOCAL_LENGTH = "focal_length"; - constexpr const char* APERTURE = "aperture"; - constexpr const char* ISO = "iso"; - constexpr const char* SHUTTER_SPEED = "shutter_speed"; - - // Timestamps - constexpr const char* DATE_TIME = "date_time"; - constexpr const char* DATE_TIME_ORIGINAL = "date_time_original"; - constexpr const char* DATE_TIME_DIGITIZED = "date_time_digitized"; - - // GPS information - constexpr const char* GPS_LATITUDE = "gps_latitude"; - constexpr const char* GPS_LONGITUDE = "gps_longitude"; - constexpr const char* GPS_ALTITUDE = "gps_altitude"; - - // File information - constexpr const char* FILE_NAME = "file_name"; - constexpr const char* FILE_SIZE = "file_size"; - constexpr const char* FILE_MODIFIED = "file_modified"; - - // Processing information - constexpr const char* SOFTWARE = "software"; - constexpr const char* PROCESSING = "processing"; - constexpr const char* COMPRESSION = "compression"; - constexpr const char* QUALITY = "quality"; - - // Copyright information - constexpr const char* COPYRIGHT = "copyright"; - constexpr const char* ARTIST = "artist"; - constexpr const char* CREATOR = "creator"; -} +// Basic image information +constexpr const char* WIDTH = "width"; +constexpr const char* HEIGHT = "height"; +constexpr const char* CHANNELS = "channels"; +constexpr const char* BIT_DEPTH = "bit_depth"; +constexpr const char* COLOR_SPACE = "color_space"; +constexpr const char* FORMAT = "format"; + +// Camera information +constexpr const char* CAMERA_MAKE = "camera_make"; +constexpr const char* CAMERA_MODEL = "camera_model"; +constexpr const char* LENS_MODEL = "lens_model"; +constexpr const char* FOCAL_LENGTH = "focal_length"; +constexpr const char* APERTURE = "aperture"; +constexpr const char* ISO = "iso"; +constexpr const char* SHUTTER_SPEED = "shutter_speed"; + +// Timestamps +constexpr const char* DATE_TIME = "date_time"; +constexpr const char* DATE_TIME_ORIGINAL = "date_time_original"; +constexpr const char* DATE_TIME_DIGITIZED = "date_time_digitized"; + +// GPS information +constexpr const char* GPS_LATITUDE = "gps_latitude"; +constexpr const char* GPS_LONGITUDE = "gps_longitude"; +constexpr const char* GPS_ALTITUDE = "gps_altitude"; + +// File information +constexpr const char* FILE_NAME = "file_name"; +constexpr const char* FILE_SIZE = "file_size"; +constexpr const char* FILE_MODIFIED = "file_modified"; + +// Processing information +constexpr const char* SOFTWARE = "software"; +constexpr const char* PROCESSING = "processing"; +constexpr const char* COMPRESSION = "compression"; +constexpr const char* QUALITY = "quality"; + +// Copyright information +constexpr const char* COPYRIGHT = "copyright"; +constexpr const char* ARTIST = "artist"; +constexpr const char* CREATOR = "creator"; +} // namespace MetadataKeys /** * @brief EXIF metadata handler @@ -279,4 +266,4 @@ class ExifMetadata { // This is a placeholder for EXIF parsing functionality }; -} // namespace atom::image::core \ No newline at end of file +} // namespace atom::image::core diff --git a/atom/image/formats/README.md b/atom/image/formats/README.md index 38905d3d..6e9d8e15 100644 --- a/atom/image/formats/README.md +++ b/atom/image/formats/README.md @@ -9,6 +9,7 @@ The formats module provides comprehensive support for reading, writing, and mani ## Supported Formats ### Standard Formats + - **JPEG**: Lossy compression, widely supported - **PNG**: Lossless compression, transparency support - **TIFF**: Flexible format, multiple compression options @@ -16,6 +17,7 @@ The formats module provides comprehensive support for reading, writing, and mani - **TGA**: Targa format, gaming industry standard ### Specialized Formats + - **FITS**: Flexible Image Transport System (astronomy) - **SER**: Simple Extensible Recorder (astronomical video) @@ -26,6 +28,7 @@ The formats module provides comprehensive support for reading, writing, and mani FITS (Flexible Image Transport System) is the standard format for astronomical images and data. #### Files + - `fits_file.hpp/cpp`: Main FITS file handling - `fits_header.hpp/cpp`: FITS header management - `fits_data.hpp/cpp`: FITS data access and manipulation @@ -33,6 +36,7 @@ FITS (Flexible Image Transport System) is the standard format for astronomical i - `hdu.hpp/cpp`: Header Data Unit (HDU) management #### Features + - **Multi-HDU Support**: Primary and extension HDUs - **Data Types**: Support for all FITS data types - **Compression**: Rice, GZIP, and other compression algorithms @@ -68,6 +72,7 @@ fits.writeFITS("processed_data.fits"); SER (Simple Extensible Recorder) is a format for astronomical video sequences. #### Components + - **SERReader**: Reading SER video files - **SERWriter**: Writing SER video files - **FrameProcessor**: Frame processing and analysis @@ -76,6 +81,7 @@ SER (Simple Extensible Recorder) is a format for astronomical video sequences. - **Stacking**: Frame combination techniques #### Features + - **High Performance**: Optimized for large video sequences - **Frame Caching**: Intelligent caching for random access - **Quality Assessment**: Automatic frame quality evaluation @@ -127,12 +133,14 @@ auto image = reader->load("image.fits"); ## Compression Support ### FITS Compression + - **Rice**: Lossless compression for integer data - **GZIP**: General-purpose compression - **HCOMPRESS**: Specialized astronomical image compression - **PLIO**: Pixel list compression ### Standard Format Compression + - **JPEG**: Quality-based lossy compression - **PNG**: Lossless compression with multiple levels - **TIFF**: Multiple compression algorithms (LZW, ZIP, etc.) @@ -190,11 +198,13 @@ try { ## Performance Considerations ### Large File Handling + - Use memory mapping for files > 100MB - Enable tiled access for images > 4K resolution - Consider streaming for video sequences ### Batch Processing + - Process multiple files in parallel - Use format-specific optimizations - Cache frequently accessed metadata @@ -219,10 +229,12 @@ output.writeFITS(); ## Dependencies ### Required + - atom-error: Error handling framework - atom-image-core: Core image functionality ### Optional + - CFITSIO: FITS file I/O library - OpenCV: Additional format support - LibTIFF: Enhanced TIFF support diff --git a/atom/image/formats/advanced_formats.cpp b/atom/image/formats/advanced_formats.cpp index 5c8c6cc9..fa1d4b1e 100644 --- a/atom/image/formats/advanced_formats.cpp +++ b/atom/image/formats/advanced_formats.cpp @@ -1,7 +1,7 @@ #include "advanced_formats.hpp" #include -#include #include +#include #include // Define error macros to avoid atom error system namespace pollution @@ -18,25 +18,28 @@ #endif #ifdef ATOM_IMAGE_HAS_OPENEXR -#include #include +#include #endif #ifdef ATOM_IMAGE_HAS_OPENCV -#include #include +#include #endif namespace atom::image { -AdvancedFormat AdvancedFormatProcessor::detectFormat(const std::string& filename) const { +AdvancedFormat AdvancedFormatProcessor::detectFormat( + const std::string& filename) const { if (!std::filesystem::exists(filename)) { return AdvancedFormat::UNKNOWN; } // Get file extension - std::string extension = std::filesystem::path(filename).extension().string(); - std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower); + std::string extension = + std::filesystem::path(filename).extension().string(); + std::transform(extension.begin(), extension.end(), extension.begin(), + ::tolower); // Remove leading dot if (!extension.empty() && extension[0] == '.') { @@ -44,59 +47,60 @@ AdvancedFormat AdvancedFormatProcessor::detectFormat(const std::string& filename } // Map extensions to formats - static const std::unordered_map extensionMap = { - // RAW formats - {"cr2", AdvancedFormat::CR2}, - {"nef", AdvancedFormat::NEF}, - {"arw", AdvancedFormat::ARW}, - {"dng", AdvancedFormat::DNG}, - {"raf", AdvancedFormat::RAF}, - {"orf", AdvancedFormat::ORF}, - {"rw2", AdvancedFormat::RW2}, - {"pef", AdvancedFormat::PEF}, - {"srw", AdvancedFormat::SRW}, - {"x3f", AdvancedFormat::X3F}, - - // Medical formats - {"dcm", AdvancedFormat::DICOM}, - {"dicom", AdvancedFormat::DICOM}, - {"nii", AdvancedFormat::NIFTI}, - {"nifti", AdvancedFormat::NIFTI}, - {"hdr", AdvancedFormat::ANALYZE}, - {"img", AdvancedFormat::ANALYZE}, - {"mnc", AdvancedFormat::MINC}, - {"nrrd", AdvancedFormat::NRRD}, - {"nhdr", AdvancedFormat::NRRD}, - - // Scientific formats - {"h5", AdvancedFormat::HDF5}, - {"hdf5", AdvancedFormat::HDF5}, - {"nc", AdvancedFormat::NETCDF}, - {"cdf", AdvancedFormat::NETCDF}, - // Note: FITS format not included in enum, would need to be added - - // HDR formats - {"exr", AdvancedFormat::OPENEXR}, - {"hdr", AdvancedFormat::RADIANCE}, - {"pic", AdvancedFormat::RADIANCE}, - {"pfm", AdvancedFormat::PFM}, - - // Modern web formats - {"avif", AdvancedFormat::AVIF}, - {"heic", AdvancedFormat::HEIF}, // HEIC is a variant of HEIF - {"heif", AdvancedFormat::HEIF}, - {"jxl", AdvancedFormat::JPEG_XL}, - - // Vector formats - {"svg", AdvancedFormat::SVG}, - {"pdf", AdvancedFormat::PDF}, - {"eps", AdvancedFormat::EPS}, - // Note: AI format not in enum - - // Animation formats - {"apng", AdvancedFormat::APNG} - // Note: MNG format not in enum - }; + static const std::unordered_map extensionMap = + { + // RAW formats + {"cr2", AdvancedFormat::CR2}, + {"nef", AdvancedFormat::NEF}, + {"arw", AdvancedFormat::ARW}, + {"dng", AdvancedFormat::DNG}, + {"raf", AdvancedFormat::RAF}, + {"orf", AdvancedFormat::ORF}, + {"rw2", AdvancedFormat::RW2}, + {"pef", AdvancedFormat::PEF}, + {"srw", AdvancedFormat::SRW}, + {"x3f", AdvancedFormat::X3F}, + + // Medical formats + {"dcm", AdvancedFormat::DICOM}, + {"dicom", AdvancedFormat::DICOM}, + {"nii", AdvancedFormat::NIFTI}, + {"nifti", AdvancedFormat::NIFTI}, + {"hdr", AdvancedFormat::ANALYZE}, + {"img", AdvancedFormat::ANALYZE}, + {"mnc", AdvancedFormat::MINC}, + {"nrrd", AdvancedFormat::NRRD}, + {"nhdr", AdvancedFormat::NRRD}, + + // Scientific formats + {"h5", AdvancedFormat::HDF5}, + {"hdf5", AdvancedFormat::HDF5}, + {"nc", AdvancedFormat::NETCDF}, + {"cdf", AdvancedFormat::NETCDF}, + // Note: FITS format not included in enum, would need to be added + + // HDR formats + {"exr", AdvancedFormat::OPENEXR}, + {"hdr", AdvancedFormat::RADIANCE}, + {"pic", AdvancedFormat::RADIANCE}, + {"pfm", AdvancedFormat::PFM}, + + // Modern web formats + {"avif", AdvancedFormat::AVIF}, + {"heic", AdvancedFormat::HEIF}, // HEIC is a variant of HEIF + {"heif", AdvancedFormat::HEIF}, + {"jxl", AdvancedFormat::JPEG_XL}, + + // Vector formats + {"svg", AdvancedFormat::SVG}, + {"pdf", AdvancedFormat::PDF}, + {"eps", AdvancedFormat::EPS}, + // Note: AI format not in enum + + // Animation formats + {"apng", AdvancedFormat::APNG} + // Note: MNG format not in enum + }; auto it = extensionMap.find(extension); if (it != extensionMap.end()) { @@ -106,45 +110,48 @@ AdvancedFormat AdvancedFormatProcessor::detectFormat(const std::string& filename return AdvancedFormat::UNKNOWN; } -AdvancedFormat AdvancedFormatProcessor::detectFormat(const uint8_t* data, size_t size) const { +AdvancedFormat AdvancedFormatProcessor::detectFormat(const uint8_t* data, + size_t size) const { if (!data || size < 16) { return AdvancedFormat::UNKNOWN; } // Check magic bytes for various formats - + // DICOM if (size > 132 && std::memcmp(data + 128, "DICM", 4) == 0) { return AdvancedFormat::DICOM; } - + // OpenEXR - if (size > 4 && data[0] == 0x76 && data[1] == 0x2f && data[2] == 0x31 && data[3] == 0x01) { + if (size > 4 && data[0] == 0x76 && data[1] == 0x2f && data[2] == 0x31 && + data[3] == 0x01) { return AdvancedFormat::OPENEXR; } // Note: FITS format detection would need FITS enum value - + // HDF5 if (size > 8 && std::memcmp(data, "\x89HDF\r\n\x1a\n", 8) == 0) { return AdvancedFormat::HDF5; } - + // PDF if (size > 4 && std::memcmp(data, "%PDF", 4) == 0) { return AdvancedFormat::PDF; } - + // SVG - if (size > 5 && (std::memcmp(data, " 5 && (std::memcmp(data, " 12 && std::memcmp(data + 4, "ftypavif", 8) == 0) { return AdvancedFormat::AVIF; } - + // HEIC/HEIF if (size > 12) { if (std::memcmp(data + 4, "ftypheic", 8) == 0) { @@ -154,7 +161,7 @@ AdvancedFormat AdvancedFormatProcessor::detectFormat(const uint8_t* data, size_t return AdvancedFormat::HEIF; } } - + // JPEG XL if (size > 12 && std::memcmp(data, "\x00\x00\x00\x0cJXL ", 12) == 0) { return AdvancedFormat::JPEG_XL; @@ -163,9 +170,9 @@ AdvancedFormat AdvancedFormatProcessor::detectFormat(const uint8_t* data, size_t return AdvancedFormat::UNKNOWN; } -blob AdvancedFormatProcessor::loadImage(const std::string& filename, - AdvancedFormat format, - const std::unordered_map& params) const { +blob AdvancedFormatProcessor::loadImage( + const std::string& filename, AdvancedFormat format, + const std::unordered_map& params) const { if (!std::filesystem::exists(filename)) { return blob{}; } @@ -191,12 +198,12 @@ blob AdvancedFormatProcessor::loadImage(const std::string& filename, // Parse RAW-specific parameters from params map return loadRAW(filename, rawParams); } - + case AdvancedFormat::DICOM: { auto result = loadDICOM(filename); return result.first; } - + case AdvancedFormat::OPENEXR: case AdvancedFormat::RADIANCE: case AdvancedFormat::PFM: { @@ -216,18 +223,18 @@ blob AdvancedFormatProcessor::loadImage(const std::string& filename, } break; } - + default: // Try to load with OpenCV as fallback #ifdef ATOM_IMAGE_HAS_OPENCV - { - cv::Mat image = cv::imread(filename, cv::IMREAD_UNCHANGED); - if (!image.empty()) { - return blob(image); - } + { + cv::Mat image = cv::imread(filename, cv::IMREAD_UNCHANGED); + if (!image.empty()) { + return blob(image); } + } #endif - break; + break; } } catch (const std::exception&) { // Handle loading errors @@ -236,10 +243,9 @@ blob AdvancedFormatProcessor::loadImage(const std::string& filename, return blob{}; } -bool AdvancedFormatProcessor::saveImage(const blob& image, - const std::string& filename, - AdvancedFormat format, - const std::unordered_map& params) const { +bool AdvancedFormatProcessor::saveImage( + const blob& image, const std::string& filename, AdvancedFormat format, + const std::unordered_map& params) const { if (image.size() == 0) { return false; } @@ -251,21 +257,23 @@ bool AdvancedFormatProcessor::saveImage(const blob& image, // Parse DICOM metadata from params return saveDICOM(image, filename, metadata); } - + case AdvancedFormat::OPENEXR: case AdvancedFormat::RADIANCE: case AdvancedFormat::PFM: { - std::string compression = params.count("compression") ? params.at("compression") : "zip"; + std::string compression = params.count("compression") + ? params.at("compression") + : "zip"; return saveHDR(image, filename, format, compression); } - + default: // Try to save with OpenCV as fallback #ifdef ATOM_IMAGE_HAS_OPENCV - { - cv::Mat mat = image.to_mat(); - return cv::imwrite(filename, mat); - } + { + cv::Mat mat = image.to_mat(); + return cv::imwrite(filename, mat); + } #else return false; #endif @@ -277,47 +285,54 @@ bool AdvancedFormatProcessor::saveImage(const blob& image, return false; } -blob AdvancedFormatProcessor::loadRAW(const std::string& filename, const RAWParams& params) const { +blob AdvancedFormatProcessor::loadRAW(const std::string& filename, + const RAWParams& params) const { #ifdef ATOM_IMAGE_HAS_LIBRAW try { LibRaw rawProcessor; - + // Set processing parameters - rawProcessor.imgdata.params.use_camera_wb = params.useCameraWhiteBalance ? 1 : 0; - rawProcessor.imgdata.params.use_auto_wb = params.useAutoWhiteBalance ? 1 : 0; + rawProcessor.imgdata.params.use_camera_wb = + params.useCameraWhiteBalance ? 1 : 0; + rawProcessor.imgdata.params.use_auto_wb = + params.useAutoWhiteBalance ? 1 : 0; rawProcessor.imgdata.params.output_color = params.colorSpace; rawProcessor.imgdata.params.output_bps = params.outputBitDepth; rawProcessor.imgdata.params.gamma_16bit = params.gamma16bit ? 1 : 0; - rawProcessor.imgdata.params.no_auto_bright = params.noAutoBright ? 1 : 0; - rawProcessor.imgdata.params.bright = static_cast(params.brightness); - + rawProcessor.imgdata.params.no_auto_bright = + params.noAutoBright ? 1 : 0; + rawProcessor.imgdata.params.bright = + static_cast(params.brightness); + // Open and process RAW file int ret = rawProcessor.open_file(filename.c_str()); if (ret != LIBRAW_SUCCESS) { return blob{}; } - + ret = rawProcessor.unpack(); if (ret != LIBRAW_SUCCESS) { return blob{}; } - + ret = rawProcessor.dcraw_process(); if (ret != LIBRAW_SUCCESS) { return blob{}; } - - libraw_processed_image_t* processedImage = rawProcessor.dcraw_make_mem_image(&ret); + + libraw_processed_image_t* processedImage = + rawProcessor.dcraw_make_mem_image(&ret); if (!processedImage) { return blob{}; } - + // Convert to blob size_t dataSize = processedImage->data_size; - std::vector imageData(processedImage->data, processedImage->data + dataSize); - + std::vector imageData(processedImage->data, + processedImage->data + dataSize); + LibRaw::dcraw_clear_mem(processedImage); - + return blob(imageData); } catch (const std::exception&) { return blob{}; @@ -328,9 +343,8 @@ blob AdvancedFormatProcessor::loadRAW(const std::string& filename, const RAWPara #endif } -std::pair AdvancedFormatProcessor::loadDICOM(const std::string& filename, - int seriesIndex, - int frameIndex) const { +std::pair AdvancedFormatProcessor::loadDICOM( + const std::string& filename, int seriesIndex, int frameIndex) const { DICOMMetadata metadata; #ifdef ATOM_IMAGE_HAS_DCMTK @@ -375,15 +389,16 @@ std::pair AdvancedFormatProcessor::loadDICOM(const std::str } // Get pixel data - const void* pixelData = dicomImage->getOutputData(8); // 8-bit output + const void* pixelData = dicomImage->getOutputData(8); // 8-bit output if (!pixelData) { delete dicomImage; return {blob{}, metadata}; } size_t dataSize = rows * cols * samples; - std::vector imageData(static_cast(pixelData), - static_cast(pixelData) + dataSize); + std::vector imageData( + static_cast(pixelData), + static_cast(pixelData) + dataSize); delete dicomImage; @@ -398,15 +413,16 @@ std::pair AdvancedFormatProcessor::loadDICOM(const std::str } bool AdvancedFormatProcessor::saveDICOM(const blob& image, - const std::string& filename, - const DICOMMetadata& metadata) const { + const std::string& filename, + const DICOMMetadata& metadata) const { #ifdef ATOM_IMAGE_HAS_DCMTK try { DcmFileFormat fileFormat; DcmDataset* dataset = fileFormat.getDataset(); // Set basic DICOM tags - dataset->putAndInsertString(DCM_PatientName, metadata.patientName.c_str()); + dataset->putAndInsertString(DCM_PatientName, + metadata.patientName.c_str()); dataset->putAndInsertString(DCM_StudyDate, metadata.studyDate.c_str()); dataset->putAndInsertString(DCM_Modality, metadata.modality.c_str()); dataset->putAndInsertUint16(DCM_Rows, metadata.height); @@ -419,7 +435,8 @@ bool AdvancedFormatProcessor::saveDICOM(const blob& image, dataset->putAndInsertUint16(DCM_PixelRepresentation, 0); // Set pixel data - dataset->putAndInsertUint8Array(DCM_PixelData, image.data(), image.size()); + dataset->putAndInsertUint8Array(DCM_PixelData, image.data(), + image.size()); // Save file OFCondition status = fileFormat.saveFile(filename.c_str()); @@ -432,7 +449,8 @@ bool AdvancedFormatProcessor::saveDICOM(const blob& image, #endif } -std::vector AdvancedFormatProcessor::loadAnimation(const std::string& filename) const { +std::vector AdvancedFormatProcessor::loadAnimation( + const std::string& filename) const { std::vector frames; #ifdef ATOM_IMAGE_HAS_OPENCV @@ -452,7 +470,7 @@ std::vector AdvancedFormatProcessor::loadAnimation(const std::st AnimationFrame animFrame; animFrame.imageData = blob(frame); - animFrame.duration = 100; // Default delay + animFrame.duration = 100; // Default delay animFrame.disposalMethod = 0; frames.push_back(animFrame); @@ -466,10 +484,9 @@ std::vector AdvancedFormatProcessor::loadAnimation(const std::st return frames; } -bool AdvancedFormatProcessor::saveAnimation(const std::vector& frames, - const std::string& filename, - AdvancedFormat format, - int loopCount) const { +bool AdvancedFormatProcessor::saveAnimation( + const std::vector& frames, const std::string& filename, + AdvancedFormat format, int loopCount) const { if (frames.empty()) { return false; } @@ -482,7 +499,9 @@ bool AdvancedFormatProcessor::saveAnimation(const std::vector& f cv::Size frameSize(firstFrame.cols, firstFrame.rows); int fourcc = cv::VideoWriter::fourcc('M', 'J', 'P', 'G'); - double fps = 1000.0 / std::max(1, frames[0].duration); // Convert delay to FPS + double fps = + 1000.0 / + std::max(1, frames[0].duration); // Convert delay to FPS cv::VideoWriter writer(filename, fourcc, fps, frameSize); if (!writer.isOpened()) { @@ -505,8 +524,7 @@ bool AdvancedFormatProcessor::saveAnimation(const std::vector& f } blob AdvancedFormatProcessor::loadHDR(const std::string& filename, - double exposure, - double gamma) const { + double exposure, double gamma) const { #ifdef ATOM_IMAGE_HAS_OPENEXR try { if (detectFormat(filename) == AdvancedFormat::EXR) { @@ -517,7 +535,8 @@ blob AdvancedFormatProcessor::loadHDR(const std::string& filename, int height = dw.max.y - dw.min.y + 1; Imf::Array2D pixels(height, width); - file.setFrameBuffer(&pixels[0][0] - dw.min.x - dw.min.y * width, 1, width); + file.setFrameBuffer(&pixels[0][0] - dw.min.x - dw.min.y * width, 1, + width); file.readPixels(dw.min.y, dw.max.y); // Convert to 8-bit with exposure and gamma correction @@ -528,9 +547,12 @@ blob AdvancedFormatProcessor::loadHDR(const std::string& filename, const Imf::Rgba& pixel = pixels[y][x]; // Apply exposure - float r = pixel.r * std::pow(2.0f, static_cast(exposure)); - float g = pixel.g * std::pow(2.0f, static_cast(exposure)); - float b = pixel.b * std::pow(2.0f, static_cast(exposure)); + float r = + pixel.r * std::pow(2.0f, static_cast(exposure)); + float g = + pixel.g * std::pow(2.0f, static_cast(exposure)); + float b = + pixel.b * std::pow(2.0f, static_cast(exposure)); // Apply gamma correction r = std::pow(r, 1.0f / static_cast(gamma)); @@ -539,9 +561,12 @@ blob AdvancedFormatProcessor::loadHDR(const std::string& filename, // Clamp and convert to 8-bit int idx = (y * width + x) * 3; - imageData[idx] = static_cast(std::clamp(r * 255.0f, 0.0f, 255.0f)); - imageData[idx + 1] = static_cast(std::clamp(g * 255.0f, 0.0f, 255.0f)); - imageData[idx + 2] = static_cast(std::clamp(b * 255.0f, 0.0f, 255.0f)); + imageData[idx] = static_cast( + std::clamp(r * 255.0f, 0.0f, 255.0f)); + imageData[idx + 1] = static_cast( + std::clamp(g * 255.0f, 0.0f, 255.0f)); + imageData[idx + 2] = static_cast( + std::clamp(b * 255.0f, 0.0f, 255.0f)); } } @@ -554,7 +579,8 @@ blob AdvancedFormatProcessor::loadHDR(const std::string& filename, // Fallback to OpenCV #ifdef ATOM_IMAGE_HAS_OPENCV - cv::Mat hdrImage = cv::imread(filename, cv::IMREAD_ANYDEPTH | cv::IMREAD_COLOR); + cv::Mat hdrImage = + cv::imread(filename, cv::IMREAD_ANYDEPTH | cv::IMREAD_COLOR); if (!hdrImage.empty()) { cv::Mat ldrImage; hdrImage.convertTo(ldrImage, CV_8UC3, 255.0); @@ -565,36 +591,37 @@ blob AdvancedFormatProcessor::loadHDR(const std::string& filename, return blob{}; } -bool AdvancedFormatProcessor::saveHDR(const blob& /*image*/, - const std::string& /*filename*/, - AdvancedFormat /*format*/, - const std::string& /*compression*/) const { +bool AdvancedFormatProcessor::saveHDR( + const blob& /*image*/, const std::string& /*filename*/, + AdvancedFormat /*format*/, const std::string& /*compression*/) const { THROW_RUNTIME_ERROR("HDR saving not available in this build"); } blob AdvancedFormatProcessor::loadVector(const std::string& /*filename*/, - int /*width*/, int /*height*/, - double /*dpi*/) const { + int /*width*/, int /*height*/, + double /*dpi*/) const { THROW_RUNTIME_ERROR("Vector loading not available in this build"); } std::pair> AdvancedFormatProcessor::loadMicroscopy(const std::string& /*filename*/, - int /*seriesIndex*/, int /*channelIndex*/, - int /*timeIndex*/, int /*zIndex*/) const { + int /*seriesIndex*/, + int /*channelIndex*/, int /*timeIndex*/, + int /*zIndex*/) const { THROW_RUNTIME_ERROR("Microscopy loading not available in this build"); } std::pair> -AdvancedFormatProcessor::loadSatellite(const std::string& /*filename*/, - const std::vector& /*bandIndices*/) const { +AdvancedFormatProcessor::loadSatellite( + const std::string& /*filename*/, + const std::vector& /*bandIndices*/) const { THROW_RUNTIME_ERROR("Satellite loading not available in this build"); } -bool AdvancedFormatProcessor::convertFormat(const std::string& /*inputFile*/, - const std::string& /*outputFile*/, - AdvancedFormat /*outputFormat*/, - const std::unordered_map& /*params*/) const { +bool AdvancedFormatProcessor::convertFormat( + const std::string& /*inputFile*/, const std::string& /*outputFile*/, + AdvancedFormat /*outputFormat*/, + const std::unordered_map& /*params*/) const { THROW_RUNTIME_ERROR("Format conversion not available in this build"); } @@ -648,15 +675,15 @@ int AdvancedFormatProcessor::batchConvert( THROW_RUNTIME_ERROR("Batch conversion not available in this build"); } -bool AdvancedFormatProcessor::initializeLibraries() const { - return false; -} +bool AdvancedFormatProcessor::initializeLibraries() const { return false; } -bool AdvancedFormatProcessor::loadFormatLibrary(AdvancedFormat /*format*/) const { +bool AdvancedFormatProcessor::loadFormatLibrary( + AdvancedFormat /*format*/) const { return false; } -std::string AdvancedFormatProcessor::getFormatName(AdvancedFormat format) const { +std::string AdvancedFormatProcessor::getFormatName( + AdvancedFormat format) const { switch (format) { case AdvancedFormat::WEBP: return "WEBP"; @@ -673,7 +700,8 @@ std::string AdvancedFormatProcessor::getFormatName(AdvancedFormat format) const } } -std::unordered_map AdvancedFormatProcessor::parseFormatParams( +std::unordered_map +AdvancedFormatProcessor::parseFormatParams( const std::unordered_map& params, AdvancedFormat /*format*/) const { return params; @@ -684,4 +712,4 @@ std::unique_ptr createOptimalFormatProcessor( return std::make_unique(); } -} // namespace atom::image +} // namespace atom::image diff --git a/atom/image/formats/advanced_formats.hpp b/atom/image/formats/advanced_formats.hpp index c95dcbcd..20581d4c 100644 --- a/atom/image/formats/advanced_formats.hpp +++ b/atom/image/formats/advanced_formats.hpp @@ -14,12 +14,12 @@ * @version 1.0.0 */ -#include "../core/image_blob.hpp" -#include -#include +#include #include +#include #include -#include +#include +#include "../core/image_blob.hpp" namespace atom::image { @@ -28,94 +28,94 @@ namespace atom::image { */ enum class AdvancedFormat { // RAW camera formats - CR2, // Canon RAW - NEF, // Nikon RAW - ARW, // Sony RAW - DNG, // Adobe Digital Negative - RAF, // Fujifilm RAW - ORF, // Olympus RAW - RW2, // Panasonic RAW - PEF, // Pentax RAW - SRW, // Samsung RAW - X3F, // Sigma RAW + CR2, // Canon RAW + NEF, // Nikon RAW + ARW, // Sony RAW + DNG, // Adobe Digital Negative + RAF, // Fujifilm RAW + ORF, // Olympus RAW + RW2, // Panasonic RAW + PEF, // Pentax RAW + SRW, // Samsung RAW + X3F, // Sigma RAW // Medical imaging formats - DICOM, // Digital Imaging and Communications in Medicine - NIFTI, // Neuroimaging Informatics Technology Initiative - ANALYZE, // Analyze format - MINC, // Medical Image NetCDF - NRRD, // Nearly Raw Raster Data + DICOM, // Digital Imaging and Communications in Medicine + NIFTI, // Neuroimaging Informatics Technology Initiative + ANALYZE, // Analyze format + MINC, // Medical Image NetCDF + NRRD, // Nearly Raw Raster Data // Scientific formats - HDF5, // Hierarchical Data Format 5 - NETCDF, // Network Common Data Form - GRIB, // Gridded Binary - MATLAB, // MATLAB format + HDF5, // Hierarchical Data Format 5 + NETCDF, // Network Common Data Form + GRIB, // Gridded Binary + MATLAB, // MATLAB format // Modern web formats - WEBP, // WebP format - AVIF, // AV1 Image File Format - HEIF, // High Efficiency Image Format - JPEG_XL, // JPEG XL + WEBP, // WebP format + AVIF, // AV1 Image File Format + HEIF, // High Efficiency Image Format + JPEG_XL, // JPEG XL // Vector formats - SVG, // Scalable Vector Graphics - PDF, // Portable Document Format - EPS, // Encapsulated PostScript + SVG, // Scalable Vector Graphics + PDF, // Portable Document Format + EPS, // Encapsulated PostScript // Archive formats - ICO, // Windows Icon - ICNS, // macOS Icon - CUR, // Windows Cursor + ICO, // Windows Icon + ICNS, // macOS Icon + CUR, // Windows Cursor // Specialized formats - OPENEXR, // OpenEXR HDR format - RADIANCE, // Radiance HDR format - PFM, // Portable Float Map - XCF, // GIMP native format - PSD, // Photoshop Document + OPENEXR, // OpenEXR HDR format + RADIANCE, // Radiance HDR format + PFM, // Portable Float Map + XCF, // GIMP native format + PSD, // Photoshop Document // Animation formats - GIF, // Graphics Interchange Format - APNG, // Animated PNG - WEBP_ANIM, // Animated WebP + GIF, // Graphics Interchange Format + APNG, // Animated PNG + WEBP_ANIM, // Animated WebP // Microscopy formats - LSM, // Zeiss LSM - CZI, // Zeiss CZI - LIF, // Leica LIF - ND2, // Nikon ND2 - OIB, // Olympus OIB + LSM, // Zeiss LSM + CZI, // Zeiss CZI + LIF, // Leica LIF + ND2, // Nikon ND2 + OIB, // Olympus OIB // Satellite/GIS formats - GEOTIFF, // GeoTIFF - NITF, // National Imagery Transmission Format - MrSID, // Multi-resolution Seamless Image Database - ECW, // Enhanced Compression Wavelet + GEOTIFF, // GeoTIFF + NITF, // National Imagery Transmission Format + MrSID, // Multi-resolution Seamless Image Database + ECW, // Enhanced Compression Wavelet - UNKNOWN // Unknown format + UNKNOWN // Unknown format }; /** * @brief RAW processing parameters */ struct RAWParams { - double exposure = 0.0; // Exposure compensation - double highlights = 0.0; // Highlight recovery - double shadows = 0.0; // Shadow recovery - double whites = 0.0; // White point adjustment - double blacks = 0.0; // Black point adjustment - double clarity = 0.0; // Clarity/structure - double vibrance = 0.0; // Vibrance adjustment - double saturation = 0.0; // Saturation adjustment - double temperature = 0.0; // Color temperature - double tint = 0.0; // Tint adjustment - double sharpness = 0.0; // Sharpening amount - double noiseReduction = 0.0; // Noise reduction - bool autoWhiteBalance = true; // Auto white balance - bool autoExposure = false; // Auto exposure - std::string colorSpace = "sRGB"; // Output color space - int bitDepth = 8; // Output bit depth + double exposure = 0.0; // Exposure compensation + double highlights = 0.0; // Highlight recovery + double shadows = 0.0; // Shadow recovery + double whites = 0.0; // White point adjustment + double blacks = 0.0; // Black point adjustment + double clarity = 0.0; // Clarity/structure + double vibrance = 0.0; // Vibrance adjustment + double saturation = 0.0; // Saturation adjustment + double temperature = 0.0; // Color temperature + double tint = 0.0; // Tint adjustment + double sharpness = 0.0; // Sharpening amount + double noiseReduction = 0.0; // Noise reduction + bool autoWhiteBalance = true; // Auto white balance + bool autoExposure = false; // Auto exposure + std::string colorSpace = "sRGB"; // Output color space + int bitDepth = 8; // Output bit depth }; /** @@ -180,9 +180,10 @@ class AdvancedFormatProcessor { * @param params Format-specific parameters * @return Loaded image blob */ - virtual blob loadImage(const std::string& filename, - AdvancedFormat format = AdvancedFormat::UNKNOWN, - const std::unordered_map& params = {}) const; + virtual blob loadImage( + const std::string& filename, + AdvancedFormat format = AdvancedFormat::UNKNOWN, + const std::unordered_map& params = {}) const; /** * @brief Save image to advanced format @@ -192,10 +193,9 @@ class AdvancedFormatProcessor { * @param params Format-specific parameters * @return Success status */ - virtual bool saveImage(const blob& image, - const std::string& filename, - AdvancedFormat format, - const std::unordered_map& params = {}) const; + virtual bool saveImage( + const blob& image, const std::string& filename, AdvancedFormat format, + const std::unordered_map& params = {}) const; /** * @brief Load RAW camera image @@ -203,7 +203,8 @@ class AdvancedFormatProcessor { * @param params RAW processing parameters * @return Processed image blob */ - virtual blob loadRAW(const std::string& filename, const RAWParams& params = {}) const; + virtual blob loadRAW(const std::string& filename, + const RAWParams& params = {}) const; /** * @brief Load DICOM medical image @@ -212,9 +213,9 @@ class AdvancedFormatProcessor { * @param frameIndex Frame index (for multi-frame files) * @return Loaded image blob and metadata */ - virtual std::pair loadDICOM(const std::string& filename, - int seriesIndex = 0, - int frameIndex = 0) const; + virtual std::pair loadDICOM( + const std::string& filename, int seriesIndex = 0, + int frameIndex = 0) const; /** * @brief Save DICOM medical image @@ -223,16 +224,16 @@ class AdvancedFormatProcessor { * @param metadata DICOM metadata * @return Success status */ - virtual bool saveDICOM(const blob& image, - const std::string& filename, - const DICOMMetadata& metadata) const; + virtual bool saveDICOM(const blob& image, const std::string& filename, + const DICOMMetadata& metadata) const; /** * @brief Load animated image (GIF, APNG, WebP) * @param filename Path to animated image file * @return Vector of animation frames */ - virtual std::vector loadAnimation(const std::string& filename) const; + virtual std::vector loadAnimation( + const std::string& filename) const; /** * @brief Save animated image @@ -243,9 +244,8 @@ class AdvancedFormatProcessor { * @return Success status */ virtual bool saveAnimation(const std::vector& frames, - const std::string& filename, - AdvancedFormat format, - int loopCount = 0) const; + const std::string& filename, + AdvancedFormat format, int loopCount = 0) const; /** * @brief Load HDR image (OpenEXR, Radiance) @@ -254,9 +254,8 @@ class AdvancedFormatProcessor { * @param gamma Gamma correction * @return HDR image blob (float data) */ - virtual blob loadHDR(const std::string& filename, - double exposure = 0.0, - double gamma = 1.0) const; + virtual blob loadHDR(const std::string& filename, double exposure = 0.0, + double gamma = 1.0) const; /** * @brief Save HDR image @@ -266,10 +265,9 @@ class AdvancedFormatProcessor { * @param compression Compression method * @return Success status */ - virtual bool saveHDR(const blob& image, - const std::string& filename, - AdvancedFormat format, - const std::string& compression = "zip") const; + virtual bool saveHDR(const blob& image, const std::string& filename, + AdvancedFormat format, + const std::string& compression = "zip") const; /** * @brief Load vector image (SVG, PDF, EPS) @@ -279,10 +277,8 @@ class AdvancedFormatProcessor { * @param dpi Resolution in DPI * @return Rasterized image blob */ - virtual blob loadVector(const std::string& filename, - int width = 0, - int height = 0, - double dpi = 96.0) const; + virtual blob loadVector(const std::string& filename, int width = 0, + int height = 0, double dpi = 96.0) const; /** * @brief Load microscopy image @@ -294,10 +290,8 @@ class AdvancedFormatProcessor { * @return Loaded image blob with metadata */ virtual std::pair> - loadMicroscopy(const std::string& filename, - int seriesIndex = 0, - int channelIndex = 0, - int timeIndex = 0, + loadMicroscopy(const std::string& filename, int seriesIndex = 0, + int channelIndex = 0, int timeIndex = 0, int zIndex = 0) const; /** @@ -318,17 +312,18 @@ class AdvancedFormatProcessor { * @param params Conversion parameters * @return Success status */ - virtual bool convertFormat(const std::string& inputFile, - const std::string& outputFile, - AdvancedFormat outputFormat, - const std::unordered_map& params = {}) const; + virtual bool convertFormat( + const std::string& inputFile, const std::string& outputFile, + AdvancedFormat outputFormat, + const std::unordered_map& params = {}) const; /** * @brief Get format information * @param filename Path to image file * @return Format information and metadata */ - virtual std::unordered_map getFormatInfo(const std::string& filename) const; + virtual std::unordered_map getFormatInfo( + const std::string& filename) const; /** * @brief Get supported formats @@ -348,7 +343,8 @@ class AdvancedFormatProcessor { * @param format Image format * @return Vector of file extensions */ - virtual std::vector getFormatExtensions(AdvancedFormat format) const; + virtual std::vector getFormatExtensions( + AdvancedFormat format) const; /** * @brief Batch convert images @@ -359,11 +355,11 @@ class AdvancedFormatProcessor { * @param progressCallback Progress callback function * @return Number of successfully converted files */ - virtual int batchConvert(const std::vector& inputFiles, - const std::string& outputDir, - AdvancedFormat outputFormat, - const std::unordered_map& params = {}, - std::function progressCallback = nullptr) const; + virtual int batchConvert( + const std::vector& inputFiles, + const std::string& outputDir, AdvancedFormat outputFormat, + const std::unordered_map& params = {}, + std::function progressCallback = nullptr) const; protected: /** @@ -392,9 +388,9 @@ class AdvancedFormatProcessor { * @param format Target format * @return Parsed parameters */ - virtual std::unordered_map - parseFormatParams(const std::unordered_map& params, - AdvancedFormat format) const; + virtual std::unordered_map parseFormatParams( + const std::unordered_map& params, + AdvancedFormat format) const; }; /** @@ -402,8 +398,9 @@ class AdvancedFormatProcessor { * @param enableAllFormats Whether to enable all available formats * @return Unique pointer to format processor */ -std::unique_ptr createOptimalFormatProcessor(bool enableAllFormats = true); +std::unique_ptr createOptimalFormatProcessor( + bool enableAllFormats = true); -} // namespace atom::image +} // namespace atom::image -#endif // ATOM_IMAGE_ADVANCED_FORMATS_HPP +#endif // ATOM_IMAGE_ADVANCED_FORMATS_HPP diff --git a/atom/image/formats/fits_utils.cpp b/atom/image/formats/fits_utils.cpp index d51430cc..68930e6a 100644 --- a/atom/image/formats/fits_utils.cpp +++ b/atom/image/formats/fits_utils.cpp @@ -14,7 +14,6 @@ #define THROW_RUNTIME_ERROR(msg) throw std::runtime_error(msg) #define THROW_INVALID_ARGUMENT(msg) throw std::invalid_argument(msg) - namespace atom { namespace image { @@ -192,7 +191,8 @@ void FitsImage::save(const std::string& filename) const { void FitsImage::load(const std::string& filename) { try { // Use the simple readFITS version to avoid ambiguity - (fitsFile.get()->*static_cast(&FITSFile::readFITS))(filename); + (fitsFile.get()->*static_cast( + &FITSFile::readFITS))(filename); // 从BITPIX确定数据类型 if (fitsFile->getHDUCount() > 0) { diff --git a/atom/image/image.hpp b/atom/image/image.hpp index fa502895..be68c508 100644 --- a/atom/image/image.hpp +++ b/atom/image/image.hpp @@ -5,8 +5,9 @@ * @file image.hpp * @brief Comprehensive image processing library for the Atom framework * - * This header provides a unified interface to all image processing functionality - * including format support, processing operations, and metadata handling. + * This header provides a unified interface to all image processing + * functionality including format support, processing operations, and metadata + * handling. * * @author Atom Framework Team * @date 2025 @@ -17,33 +18,33 @@ #include "core/image_blob.hpp" // Image processing operations -#include "processing/image_processor.hpp" -#include "processing/filters.hpp" -#include "processing/transforms.hpp" -#include "processing/enhancement.hpp" #include "processing/computer_vision.hpp" +#include "processing/enhancement.hpp" +#include "processing/filters.hpp" +#include "processing/gpu_acceleration.hpp" +#include "processing/image_processor.hpp" #include "processing/ml_processing.hpp" #include "processing/realtime.hpp" -#include "processing/gpu_acceleration.hpp" +#include "processing/transforms.hpp" #ifdef ATOM_IMAGE_HAS_OCR #include "processing/ocr/ocr.hpp" #endif // Format support +#include "formats/advanced_formats.hpp" +#include "formats/fits_data.hpp" #include "formats/fits_file.hpp" #include "formats/fits_header.hpp" -#include "formats/fits_data.hpp" #include "formats/fits_utils.hpp" #include "formats/hdu.hpp" -#include "formats/advanced_formats.hpp" #ifdef ATOM_IMAGE_HAS_OPENCV +#include "formats/ser/frame_processor.h" +#include "formats/ser/quality.h" #include "formats/ser/ser.hpp" #include "formats/ser/ser_reader.h" #include "formats/ser/ser_writer.h" -#include "formats/ser/frame_processor.h" -#include "formats/ser/quality.h" #endif // Metadata handling @@ -69,23 +70,23 @@ struct Version { * @brief Feature availability flags */ struct Features { - #ifdef ATOM_IMAGE_HAS_OPENCV +#ifdef ATOM_IMAGE_HAS_OPENCV static constexpr bool HAS_OPENCV = true; - #else +#else static constexpr bool HAS_OPENCV = false; - #endif +#endif - #ifdef ATOM_IMAGE_HAS_CFITSIO +#ifdef ATOM_IMAGE_HAS_CFITSIO static constexpr bool HAS_CFITSIO = true; - #else +#else static constexpr bool HAS_CFITSIO = false; - #endif +#endif - #ifdef ATOM_IMAGE_HAS_OCR +#ifdef ATOM_IMAGE_HAS_OCR static constexpr bool HAS_OCR = true; - #else +#else static constexpr bool HAS_OCR = false; - #endif +#endif }; /** @@ -103,18 +104,14 @@ void cleanup(); * @brief Get module version information * @return Version structure with version details */ -constexpr Version getVersion() { - return Version{}; -} +constexpr Version getVersion() { return Version{}; } /** * @brief Get available features * @return Features structure with capability flags */ -constexpr Features getFeatures() { - return Features{}; -} +constexpr Features getFeatures() { return Features{}; } -} // namespace atom::image +} // namespace atom::image -#endif // ATOM_IMAGE_HPP +#endif // ATOM_IMAGE_HPP diff --git a/atom/image/io/README.md b/atom/image/io/README.md index 4a7c74bf..0783323f 100644 --- a/atom/image/io/README.md +++ b/atom/image/io/README.md @@ -13,6 +13,7 @@ The I/O module provides efficient and flexible image file input/output operation Automatic detection of image file formats based on file headers, magic numbers, and file extensions. #### Features + - **Magic Number Detection**: Reliable format identification from file headers - **Extension Fallback**: Format detection from file extensions when headers are unavailable - **Confidence Scoring**: Probability-based format detection for ambiguous cases @@ -47,6 +48,7 @@ if (result.confidence > 0.9) { Efficient streaming operations for large image files and video sequences. #### Features + - **Progressive Loading**: Load images progressively for immediate display - **Memory-Mapped I/O**: Efficient access to large files without loading into memory - **Chunked Processing**: Process large images in manageable chunks @@ -80,6 +82,7 @@ auto pixel = mmap.getPixel(x, y); // Direct access without loading Efficient processing of multiple image files with parallel I/O. #### Features + - **Parallel Loading**: Load multiple images simultaneously - **Queue Management**: Intelligent queuing for optimal resource usage - **Progress Tracking**: Comprehensive progress reporting for batch operations @@ -116,6 +119,7 @@ auto images = loader.loadAll(); Integration with various file systems and storage backends. #### Features + - **Local File System**: Standard file operations - **Network Storage**: HTTP/HTTPS, FTP, cloud storage - **Archive Support**: ZIP, TAR, and other archive formats @@ -222,6 +226,7 @@ try { ## Format Support ### Standard Formats + - **JPEG**: Full support with quality control - **PNG**: Complete support including transparency - **TIFF**: Multi-page and compression support @@ -230,12 +235,14 @@ try { - **HEIF**: High-efficiency image format ### Specialized Formats + - **FITS**: Astronomical image format - **SER**: Astronomical video sequences - **RAW**: Camera raw formats (CR2, NEF, ARW, etc.) - **Medical**: DICOM and other medical imaging formats ### Archive Formats + - **ZIP**: Standard archive format - **TAR**: Unix archive format - **7Z**: High-compression archive format @@ -280,10 +287,12 @@ monitor.endOperation("large_file.tiff"); ## Dependencies ### Required + - atom-error: Error handling framework - atom-image-core: Core image functionality ### Optional + - libcurl: HTTP/HTTPS support - zlib: Compression support - liblzma: LZMA compression diff --git a/atom/image/io/format_detector.cpp b/atom/image/io/format_detector.cpp index 40be5f28..ee4dc0cf 100644 --- a/atom/image/io/format_detector.cpp +++ b/atom/image/io/format_detector.cpp @@ -1,8 +1,8 @@ #include "format_detector.hpp" -#include #include #include #include +#include #include // Define error macros to avoid atom error system namespace pollution @@ -10,137 +10,193 @@ namespace atom::image { -FormatDetector::FormatDetector() { - initializeSignatures(); -} +FormatDetector::FormatDetector() { initializeSignatures(); } void FormatDetector::initializeSignatures() { // JPEG signatures - signatures_.push_back({{0xFF, 0xD8, 0xFF}, 0, ImageFormat::JPEG, DetectionConfidence::CERTAIN, "JPEG image"}); - + signatures_.push_back({{0xFF, 0xD8, 0xFF}, + 0, + ImageFormat::JPEG, + DetectionConfidence::CERTAIN, + "JPEG image"}); + // PNG signature - signatures_.push_back({{0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}, 0, ImageFormat::PNG, DetectionConfidence::CERTAIN, "PNG image"}); - + signatures_.push_back({{0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}, + 0, + ImageFormat::PNG, + DetectionConfidence::CERTAIN, + "PNG image"}); + // BMP signatures - signatures_.push_back({{0x42, 0x4D}, 0, ImageFormat::BMP, DetectionConfidence::CERTAIN, "BMP image"}); - + signatures_.push_back({{0x42, 0x4D}, + 0, + ImageFormat::BMP, + DetectionConfidence::CERTAIN, + "BMP image"}); + // TIFF signatures (little and big endian) - signatures_.push_back({{0x49, 0x49, 0x2A, 0x00}, 0, ImageFormat::TIFF, DetectionConfidence::CERTAIN, "TIFF image (little endian)"}); - signatures_.push_back({{0x4D, 0x4D, 0x00, 0x2A}, 0, ImageFormat::TIFF, DetectionConfidence::CERTAIN, "TIFF image (big endian)"}); - + signatures_.push_back({{0x49, 0x49, 0x2A, 0x00}, + 0, + ImageFormat::TIFF, + DetectionConfidence::CERTAIN, + "TIFF image (little endian)"}); + signatures_.push_back({{0x4D, 0x4D, 0x00, 0x2A}, + 0, + ImageFormat::TIFF, + DetectionConfidence::CERTAIN, + "TIFF image (big endian)"}); + // GIF signatures - signatures_.push_back({{0x47, 0x49, 0x46, 0x38, 0x37, 0x61}, 0, ImageFormat::GIF, DetectionConfidence::CERTAIN, "GIF87a image"}); - signatures_.push_back({{0x47, 0x49, 0x46, 0x38, 0x39, 0x61}, 0, ImageFormat::GIF, DetectionConfidence::CERTAIN, "GIF89a image"}); - + signatures_.push_back({{0x47, 0x49, 0x46, 0x38, 0x37, 0x61}, + 0, + ImageFormat::GIF, + DetectionConfidence::CERTAIN, + "GIF87a image"}); + signatures_.push_back({{0x47, 0x49, 0x46, 0x38, 0x39, 0x61}, + 0, + ImageFormat::GIF, + DetectionConfidence::CERTAIN, + "GIF89a image"}); + // WebP signature - signatures_.push_back({{0x52, 0x49, 0x46, 0x46}, 0, ImageFormat::WEBP, DetectionConfidence::HIGH, "WebP container"}); - signatures_.push_back({{0x57, 0x45, 0x42, 0x50}, 8, ImageFormat::WEBP, DetectionConfidence::CERTAIN, "WebP image"}); - + signatures_.push_back({{0x52, 0x49, 0x46, 0x46}, + 0, + ImageFormat::WEBP, + DetectionConfidence::HIGH, + "WebP container"}); + signatures_.push_back({{0x57, 0x45, 0x42, 0x50}, + 8, + ImageFormat::WEBP, + DetectionConfidence::CERTAIN, + "WebP image"}); + // AVIF signature (ftyp box with AVIF brand) - signatures_.push_back({{0x66, 0x74, 0x79, 0x70, 0x61, 0x76, 0x69, 0x66}, 4, ImageFormat::AVIF, DetectionConfidence::CERTAIN, "AVIF image"}); - + signatures_.push_back({{0x66, 0x74, 0x79, 0x70, 0x61, 0x76, 0x69, 0x66}, + 4, + ImageFormat::AVIF, + DetectionConfidence::CERTAIN, + "AVIF image"}); + // HEIF signature - signatures_.push_back({{0x66, 0x74, 0x79, 0x70, 0x68, 0x65, 0x69, 0x63}, 4, ImageFormat::HEIF, DetectionConfidence::CERTAIN, "HEIF image"}); - + signatures_.push_back({{0x66, 0x74, 0x79, 0x70, 0x68, 0x65, 0x69, 0x63}, + 4, + ImageFormat::HEIF, + DetectionConfidence::CERTAIN, + "HEIF image"}); + // TGA signature (no reliable magic number, check footer) - // Note: TGA footer detection would need special handling for negative offsets - + // Note: TGA footer detection would need special handling for negative + // offsets + // PSD signature - signatures_.push_back({{0x38, 0x42, 0x50, 0x53}, 0, ImageFormat::PSD, DetectionConfidence::CERTAIN, "Photoshop PSD"}); - + signatures_.push_back({{0x38, 0x42, 0x50, 0x53}, + 0, + ImageFormat::PSD, + DetectionConfidence::CERTAIN, + "Photoshop PSD"}); + // HDR signature - signatures_.push_back({{0x23, 0x3F, 0x52, 0x41, 0x44, 0x49, 0x41, 0x4E, 0x43, 0x45, 0x0A}, 0, ImageFormat::HDR, DetectionConfidence::CERTAIN, "Radiance HDR"}); - + signatures_.push_back( + {{0x23, 0x3F, 0x52, 0x41, 0x44, 0x49, 0x41, 0x4E, 0x43, 0x45, 0x0A}, + 0, + ImageFormat::HDR, + DetectionConfidence::CERTAIN, + "Radiance HDR"}); + // EXR signature - signatures_.push_back({{0x76, 0x2F, 0x31, 0x01}, 0, ImageFormat::EXR, DetectionConfidence::CERTAIN, "OpenEXR image"}); - + signatures_.push_back({{0x76, 0x2F, 0x31, 0x01}, + 0, + ImageFormat::EXR, + DetectionConfidence::CERTAIN, + "OpenEXR image"}); + // FITS signature - signatures_.push_back({{0x53, 0x49, 0x4D, 0x50, 0x4C, 0x45, 0x20, 0x20, 0x3D, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x54}, 0, ImageFormat::FITS, DetectionConfidence::CERTAIN, "FITS astronomical image"}); - + signatures_.push_back( + {{0x53, 0x49, 0x4D, 0x50, 0x4C, 0x45, 0x20, 0x20, 0x3D, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x54}, + 0, + ImageFormat::FITS, + DetectionConfidence::CERTAIN, + "FITS astronomical image"}); + // SER signature - signatures_.push_back({{0x4C, 0x55, 0x43, 0x41, 0x4D, 0x2D, 0x52, 0x45, 0x43, 0x4F, 0x52, 0x44, 0x45, 0x52}, 0, ImageFormat::SER, DetectionConfidence::CERTAIN, "SER video sequence"}); - + signatures_.push_back({{0x4C, 0x55, 0x43, 0x41, 0x4D, 0x2D, 0x52, 0x45, + 0x43, 0x4F, 0x52, 0x44, 0x45, 0x52}, + 0, + ImageFormat::SER, + DetectionConfidence::CERTAIN, + "SER video sequence"}); + // DICOM signature - signatures_.push_back({{0x44, 0x49, 0x43, 0x4D}, 128, ImageFormat::DCM, DetectionConfidence::CERTAIN, "DICOM medical image"}); + signatures_.push_back({{0x44, 0x49, 0x43, 0x4D}, + 128, + ImageFormat::DCM, + DetectionConfidence::CERTAIN, + "DICOM medical image"}); // Initialize extension mappings - extensionMap_ = { - {"jpg", ImageFormat::JPEG}, {"jpeg", ImageFormat::JPEG}, {"jpe", ImageFormat::JPEG}, - {"png", ImageFormat::PNG}, - {"bmp", ImageFormat::BMP}, {"dib", ImageFormat::BMP}, - {"tif", ImageFormat::TIFF}, {"tiff", ImageFormat::TIFF}, - {"gif", ImageFormat::GIF}, - {"webp", ImageFormat::WEBP}, - {"avif", ImageFormat::AVIF}, - {"heif", ImageFormat::HEIF}, {"heic", ImageFormat::HEIF}, - {"tga", ImageFormat::TGA}, {"targa", ImageFormat::TGA}, - {"psd", ImageFormat::PSD}, - {"hdr", ImageFormat::HDR}, {"pic", ImageFormat::HDR}, - {"exr", ImageFormat::EXR}, - {"fits", ImageFormat::FITS}, {"fit", ImageFormat::FITS}, {"fts", ImageFormat::FITS}, - {"ser", ImageFormat::SER}, - {"dcm", ImageFormat::DCM}, {"dicom", ImageFormat::DCM}, - {"nii", ImageFormat::NII}, {"nii.gz", ImageFormat::NII}, - {"h5", ImageFormat::HDF5}, {"hdf5", ImageFormat::HDF5}, - {"raw", ImageFormat::RAW}, {"dng", ImageFormat::DNG}, - {"cr2", ImageFormat::CR2}, {"nef", ImageFormat::NEF}, - {"arw", ImageFormat::ARW}, {"orf", ImageFormat::ORF}, - {"rw2", ImageFormat::RW2}, {"raf", ImageFormat::RAF}, - {"pef", ImageFormat::PEF}, {"x3f", ImageFormat::X3F} - }; + extensionMap_ = {{"jpg", ImageFormat::JPEG}, {"jpeg", ImageFormat::JPEG}, + {"jpe", ImageFormat::JPEG}, {"png", ImageFormat::PNG}, + {"bmp", ImageFormat::BMP}, {"dib", ImageFormat::BMP}, + {"tif", ImageFormat::TIFF}, {"tiff", ImageFormat::TIFF}, + {"gif", ImageFormat::GIF}, {"webp", ImageFormat::WEBP}, + {"avif", ImageFormat::AVIF}, {"heif", ImageFormat::HEIF}, + {"heic", ImageFormat::HEIF}, {"tga", ImageFormat::TGA}, + {"targa", ImageFormat::TGA}, {"psd", ImageFormat::PSD}, + {"hdr", ImageFormat::HDR}, {"pic", ImageFormat::HDR}, + {"exr", ImageFormat::EXR}, {"fits", ImageFormat::FITS}, + {"fit", ImageFormat::FITS}, {"fts", ImageFormat::FITS}, + {"ser", ImageFormat::SER}, {"dcm", ImageFormat::DCM}, + {"dicom", ImageFormat::DCM}, {"nii", ImageFormat::NII}, + {"nii.gz", ImageFormat::NII}, {"h5", ImageFormat::HDF5}, + {"hdf5", ImageFormat::HDF5}, {"raw", ImageFormat::RAW}, + {"dng", ImageFormat::DNG}, {"cr2", ImageFormat::CR2}, + {"nef", ImageFormat::NEF}, {"arw", ImageFormat::ARW}, + {"orf", ImageFormat::ORF}, {"rw2", ImageFormat::RW2}, + {"raf", ImageFormat::RAF}, {"pef", ImageFormat::PEF}, + {"x3f", ImageFormat::X3F}}; // Initialize MIME type mappings - mimeTypeMap_ = { - {ImageFormat::JPEG, "image/jpeg"}, - {ImageFormat::PNG, "image/png"}, - {ImageFormat::BMP, "image/bmp"}, - {ImageFormat::TIFF, "image/tiff"}, - {ImageFormat::GIF, "image/gif"}, - {ImageFormat::WEBP, "image/webp"}, - {ImageFormat::AVIF, "image/avif"}, - {ImageFormat::HEIF, "image/heif"}, - {ImageFormat::TGA, "image/x-tga"}, - {ImageFormat::PSD, "image/vnd.adobe.photoshop"}, - {ImageFormat::HDR, "image/vnd.radiance"}, - {ImageFormat::EXR, "image/x-exr"}, - {ImageFormat::FITS, "image/fits"}, - {ImageFormat::SER, "application/x-ser"}, - {ImageFormat::DCM, "application/dicom"}, - {ImageFormat::RAW, "image/x-canon-raw"}, - {ImageFormat::DNG, "image/x-adobe-dng"} - }; + mimeTypeMap_ = {{ImageFormat::JPEG, "image/jpeg"}, + {ImageFormat::PNG, "image/png"}, + {ImageFormat::BMP, "image/bmp"}, + {ImageFormat::TIFF, "image/tiff"}, + {ImageFormat::GIF, "image/gif"}, + {ImageFormat::WEBP, "image/webp"}, + {ImageFormat::AVIF, "image/avif"}, + {ImageFormat::HEIF, "image/heif"}, + {ImageFormat::TGA, "image/x-tga"}, + {ImageFormat::PSD, "image/vnd.adobe.photoshop"}, + {ImageFormat::HDR, "image/vnd.radiance"}, + {ImageFormat::EXR, "image/x-exr"}, + {ImageFormat::FITS, "image/fits"}, + {ImageFormat::SER, "application/x-ser"}, + {ImageFormat::DCM, "application/dicom"}, + {ImageFormat::RAW, "image/x-canon-raw"}, + {ImageFormat::DNG, "image/x-adobe-dng"}}; // Initialize format names formatNameMap_ = { - {ImageFormat::UNKNOWN, "Unknown"}, - {ImageFormat::JPEG, "JPEG"}, - {ImageFormat::PNG, "PNG"}, - {ImageFormat::BMP, "BMP"}, - {ImageFormat::TIFF, "TIFF"}, - {ImageFormat::GIF, "GIF"}, - {ImageFormat::WEBP, "WebP"}, - {ImageFormat::AVIF, "AVIF"}, - {ImageFormat::HEIF, "HEIF"}, - {ImageFormat::TGA, "TGA"}, - {ImageFormat::PSD, "PSD"}, - {ImageFormat::HDR, "HDR"}, - {ImageFormat::EXR, "EXR"}, - {ImageFormat::FITS, "FITS"}, - {ImageFormat::SER, "SER"}, - {ImageFormat::DCM, "DICOM"}, - {ImageFormat::RAW, "RAW"}, - {ImageFormat::DNG, "DNG"} - }; + {ImageFormat::UNKNOWN, "Unknown"}, {ImageFormat::JPEG, "JPEG"}, + {ImageFormat::PNG, "PNG"}, {ImageFormat::BMP, "BMP"}, + {ImageFormat::TIFF, "TIFF"}, {ImageFormat::GIF, "GIF"}, + {ImageFormat::WEBP, "WebP"}, {ImageFormat::AVIF, "AVIF"}, + {ImageFormat::HEIF, "HEIF"}, {ImageFormat::TGA, "TGA"}, + {ImageFormat::PSD, "PSD"}, {ImageFormat::HDR, "HDR"}, + {ImageFormat::EXR, "EXR"}, {ImageFormat::FITS, "FITS"}, + {ImageFormat::SER, "SER"}, {ImageFormat::DCM, "DICOM"}, + {ImageFormat::RAW, "RAW"}, {ImageFormat::DNG, "DNG"}}; } -FormatDetectionResult FormatDetector::detectFromFile(const std::filesystem::path& filePath) const { +FormatDetectionResult FormatDetector::detectFromFile( + const std::filesystem::path& filePath) const { if (!std::filesystem::exists(filePath)) { THROW_RUNTIME_ERROR("File does not exist: " + filePath.string()); } // First try extension-based detection auto extensionResult = detectFromExtension(filePath.extension().string()); - + // Then try magic number detection std::ifstream file(filePath, std::ios::binary); if (!file.is_open()) { @@ -154,7 +210,7 @@ FormatDetectionResult FormatDetector::detectFromFile(const std::filesystem::path size_t bytesRead = static_cast(file.gcount()); auto magicResult = detectFromMemory(buffer.data(), bytesRead); - + // Combine results - prefer magic number detection if confident FormatDetectionResult result; if (magicResult.confidence >= DetectionConfidence::HIGH) { @@ -163,22 +219,25 @@ FormatDetectionResult FormatDetector::detectFromFile(const std::filesystem::path result = extensionResult; // But update confidence if magic detection found something if (magicResult.format != ImageFormat::UNKNOWN) { - result.confidence = std::min(result.confidence, magicResult.confidence); + result.confidence = + std::min(result.confidence, magicResult.confidence); } } else { - result = magicResult.format != ImageFormat::UNKNOWN ? magicResult : extensionResult; + result = magicResult.format != ImageFormat::UNKNOWN ? magicResult + : extensionResult; } return result; } -FormatDetectionResult FormatDetector::detectFromMemory(const void* data, size_t size) const { +FormatDetectionResult FormatDetector::detectFromMemory(const void* data, + size_t size) const { if (!data || size == 0) { return {}; } const auto* bytes = static_cast(data); - + // Check all signatures for (const auto& signature : signatures_) { if (checkSignature(bytes, size, signature)) { @@ -196,22 +255,26 @@ FormatDetectionResult FormatDetector::detectFromMemory(const void* data, size_t return {}; } -FormatDetectionResult FormatDetector::detectFromBlob(const blob& imageBlob) const { +FormatDetectionResult FormatDetector::detectFromBlob( + const blob& imageBlob) const { // Use begin() since data() method may not be available - return detectFromMemory(reinterpret_cast(&(*imageBlob.begin())), imageBlob.size()); + return detectFromMemory( + reinterpret_cast(&(*imageBlob.begin())), + imageBlob.size()); } -FormatDetectionResult FormatDetector::detectFromExtension(const std::string& extension) const { +FormatDetectionResult FormatDetector::detectFromExtension( + const std::string& extension) const { std::string ext = extension; - + // Remove leading dot if present if (!ext.empty() && ext[0] == '.') { ext = ext.substr(1); } - + // Convert to lowercase std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower); - + auto it = extensionMap_.find(ext); if (it != extensionMap_.end()) { FormatDetectionResult result; @@ -231,7 +294,8 @@ std::string FormatDetector::getMimeType(ImageFormat format) const { return it != mimeTypeMap_.end() ? it->second : "application/octet-stream"; } -std::vector FormatDetector::getExtensions(ImageFormat format) const { +std::vector FormatDetector::getExtensions( + ImageFormat format) const { std::vector extensions; for (const auto& [ext, fmt] : extensionMap_) { if (fmt == format) { @@ -287,9 +351,10 @@ std::vector FormatDetector::getSupportedFormats() const { return formats; } -bool FormatDetector::checkSignature(const uint8_t* data, size_t size, const MagicSignature& signature) const { +bool FormatDetector::checkSignature(const uint8_t* data, size_t size, + const MagicSignature& signature) const { size_t offset = signature.offset; - + // Handle negative offsets (from end of file) if (signature.offset < 0) { if (size < static_cast(-signature.offset)) { @@ -297,19 +362,19 @@ bool FormatDetector::checkSignature(const uint8_t* data, size_t size, const Magi } offset = size + signature.offset; } - + if (offset + signature.signature.size() > size) { return false; } - - return std::equal(signature.signature.begin(), signature.signature.end(), data + offset); + + return std::equal(signature.signature.begin(), signature.signature.end(), + data + offset); } std::unordered_map FormatDetector::analyzeHeader( const uint8_t* data, size_t size, ImageFormat format) const { - std::unordered_map metadata; - + // Basic format-specific header analysis switch (format) { case ImageFormat::JPEG: @@ -328,7 +393,7 @@ std::unordered_map FormatDetector::analyzeHeader( metadata["format"] = getFormatName(format); break; } - + return metadata; } diff --git a/atom/image/io/image_loader.cpp b/atom/image/io/image_loader.cpp index 97438939..b036cc71 100644 --- a/atom/image/io/image_loader.cpp +++ b/atom/image/io/image_loader.cpp @@ -1,18 +1,18 @@ #include "image_loader.hpp" -#include -#include #include #include #include +#include #include +#include // Define error macros to avoid atom error system namespace pollution #define THROW_RUNTIME_ERROR(msg) throw std::runtime_error(msg) #include #ifdef ATOM_IMAGE_HAS_OPENCV -#include #include +#include #endif #ifdef ATOM_IMAGE_HAS_STB @@ -24,8 +24,8 @@ namespace atom::image { ImageLoader::ImageLoader() : formatDetector_(createFormatDetector()) {} LoadResult ImageLoader::loadFromFile(const std::filesystem::path& filePath, - const LoadOptions& options, - ProgressCallback progressCallback) const { + const LoadOptions& options, + ProgressCallback progressCallback) const { auto startTime = std::chrono::high_resolution_clock::now(); LoadResult result; @@ -41,15 +41,17 @@ LoadResult ImageLoader::loadFromFile(const std::filesystem::path& filePath, } // Check cache first - std::string cacheKey = options.cacheKey.empty() ? filePath.string() : options.cacheKey; + std::string cacheKey = + options.cacheKey.empty() ? filePath.string() : options.cacheKey; if (options.enableCaching) { auto cacheIt = imageCache_.find(cacheKey); if (cacheIt != imageCache_.end()) { result.imageData = cacheIt->second; result.success = true; - result.detectedFormat = formatDetector_->detectFromFile(filePath).format; + result.detectedFormat = + formatDetector_->detectFromFile(filePath).format; cacheHits_++; - + if (progressCallback) { progressCallback(1.0f, "Loaded from cache"); } @@ -80,7 +82,8 @@ LoadResult ImageLoader::loadFromFile(const std::filesystem::path& filePath, } // Detect format - auto detectionResult = formatDetector_->detectFromMemory(fileData.data(), fileSize); + auto detectionResult = + formatDetector_->detectFromMemory(fileData.data(), fileSize); result.detectedFormat = detectionResult.format; result.metadata = detectionResult.metadata; @@ -94,7 +97,8 @@ LoadResult ImageLoader::loadFromFile(const std::filesystem::path& filePath, } // Load the image - auto loadResult = loadFormat(fileData.data(), fileSize, detectionResult.format, options); + auto loadResult = loadFormat(fileData.data(), fileSize, + detectionResult.format, options); result.imageData = loadResult.imageData; result.success = loadResult.success; result.errorMessage = loadResult.errorMessage; @@ -108,7 +112,8 @@ LoadResult ImageLoader::loadFromFile(const std::filesystem::path& filePath, result.imageData = applyPostProcessing(result.imageData, options); // Cache the result - if (options.enableCaching && cacheSize_ + result.imageData.size() <= maxCacheSize_) { + if (options.enableCaching && + cacheSize_ + result.imageData.size() <= maxCacheSize_) { imageCache_[cacheKey] = result.imageData; cacheSize_ += result.imageData.size(); } @@ -124,14 +129,15 @@ LoadResult ImageLoader::loadFromFile(const std::filesystem::path& filePath, } auto endTime = std::chrono::high_resolution_clock::now(); - result.loadTime = std::chrono::duration_cast(endTime - startTime); + result.loadTime = std::chrono::duration_cast( + endTime - startTime); return result; } -LoadResult ImageLoader::loadFromMemory(const void* data, size_t size, - const LoadOptions& options, - ProgressCallback progressCallback) const { +LoadResult ImageLoader::loadFromMemory( + const void* data, size_t size, const LoadOptions& options, + ProgressCallback progressCallback) const { auto startTime = std::chrono::high_resolution_clock::now(); LoadResult result; @@ -164,7 +170,8 @@ LoadResult ImageLoader::loadFromMemory(const void* data, size_t size, } // Load the image - auto loadResult = loadFormat(data, size, detectionResult.format, options); + auto loadResult = + loadFormat(data, size, detectionResult.format, options); result.imageData = loadResult.imageData; result.success = loadResult.success; result.errorMessage = loadResult.errorMessage; @@ -188,30 +195,33 @@ LoadResult ImageLoader::loadFromMemory(const void* data, size_t size, } auto endTime = std::chrono::high_resolution_clock::now(); - result.loadTime = std::chrono::duration_cast(endTime - startTime); + result.loadTime = std::chrono::duration_cast( + endTime - startTime); return result; } LoadResult ImageLoader::loadFromURL(const std::string& url, - const LoadOptions& options, - ProgressCallback progressCallback) const { + const LoadOptions& options, + ProgressCallback progressCallback) const { LoadResult result; result.errorMessage = "URL loading not implemented yet"; result.success = false; return result; } -BatchLoadResult ImageLoader::loadBatch(const std::vector& filePaths, - const LoadOptions& options, - size_t maxConcurrency, - ProgressCallback progressCallback) const { +BatchLoadResult ImageLoader::loadBatch( + const std::vector& filePaths, + const LoadOptions& options, size_t maxConcurrency, + ProgressCallback progressCallback) const { auto startTime = std::chrono::high_resolution_clock::now(); BatchLoadResult batchResult; batchResult.results.resize(filePaths.size()); // Limit concurrency to reasonable bounds - maxConcurrency = (maxConcurrency < std::thread::hardware_concurrency()) ? maxConcurrency : std::thread::hardware_concurrency(); + maxConcurrency = (maxConcurrency < std::thread::hardware_concurrency()) + ? maxConcurrency + : std::thread::hardware_concurrency(); maxConcurrency = (maxConcurrency > size_t(1)) ? maxConcurrency : size_t(1); std::atomic completedCount{0}; @@ -219,43 +229,50 @@ BatchLoadResult ImageLoader::loadBatch(const std::vector& std::atomic failureCount{0}; // Process files in parallel - std::for_each(std::execution::par_unseq, filePaths.begin(), filePaths.end(), - [&](const auto& filePath) { - size_t index = &filePath - &filePaths[0]; - - auto result = loadFromFile(filePath, options); - batchResult.results[index] = result; - - if (result.success) { - successCount++; - } else { - failureCount++; - } - - completedCount++; - - if (progressCallback) { - float progress = static_cast(completedCount.load()) / filePaths.size(); - progressCallback(progress, "Processed " + std::to_string(completedCount.load()) + - " of " + std::to_string(filePaths.size()) + " files"); - } - }); + std::for_each( + std::execution::par_unseq, filePaths.begin(), filePaths.end(), + [&](const auto& filePath) { + size_t index = &filePath - &filePaths[0]; + + auto result = loadFromFile(filePath, options); + batchResult.results[index] = result; + + if (result.success) { + successCount++; + } else { + failureCount++; + } + + completedCount++; + + if (progressCallback) { + float progress = static_cast(completedCount.load()) / + filePaths.size(); + progressCallback( + progress, + "Processed " + std::to_string(completedCount.load()) + + " of " + std::to_string(filePaths.size()) + " files"); + } + }); batchResult.successCount = successCount; batchResult.failureCount = failureCount; auto endTime = std::chrono::high_resolution_clock::now(); - batchResult.totalTime = std::chrono::duration_cast(endTime - startTime); + batchResult.totalTime = + std::chrono::duration_cast(endTime - + startTime); return batchResult; } -std::future ImageLoader::loadAsync(const std::filesystem::path& filePath, - const LoadOptions& options, - ProgressCallback progressCallback) const { - return std::async(std::launch::async, [this, filePath, options, progressCallback]() { - return loadFromFile(filePath, options, progressCallback); - }); +std::future ImageLoader::loadAsync( + const std::filesystem::path& filePath, const LoadOptions& options, + ProgressCallback progressCallback) const { + return std::async( + std::launch::async, [this, filePath, options, progressCallback]() { + return loadFromFile(filePath, options, progressCallback); + }); } bool ImageLoader::canLoad(const std::filesystem::path& filePath) const { @@ -264,7 +281,7 @@ bool ImageLoader::canLoad(const std::filesystem::path& filePath) const { } auto detectionResult = formatDetector_->detectFromFile(filePath); - return detectionResult.format != ImageFormat::UNKNOWN && + return detectionResult.format != ImageFormat::UNKNOWN && formatDetector_->isReadSupported(detectionResult.format); } @@ -274,7 +291,7 @@ std::vector ImageLoader::getSupportedFormats() const { void ImageLoader::setCacheSize(size_t maxSize) { maxCacheSize_ = maxSize; - + // Clear cache if current size exceeds new limit if (cacheSize_ > maxCacheSize_) { clearCache(); @@ -287,22 +304,22 @@ void ImageLoader::clearCache() { } std::unordered_map ImageLoader::getCacheStats() const { - return { - {"cache_hits", cacheHits_}, - {"cache_misses", cacheMisses_}, - {"cache_size_bytes", cacheSize_}, - {"cache_entries", imageCache_.size()}, - {"max_cache_size_bytes", maxCacheSize_} - }; + return {{"cache_hits", cacheHits_}, + {"cache_misses", cacheMisses_}, + {"cache_size_bytes", cacheSize_}, + {"cache_entries", imageCache_.size()}, + {"max_cache_size_bytes", maxCacheSize_}}; } -void ImageLoader::registerCustomLoader(ImageFormat format, - std::function loader) { +void ImageLoader::registerCustomLoader( + ImageFormat format, + std::function loader) { customLoaders_[format] = loader; } LoadResult ImageLoader::loadFormat(const void* data, size_t size, - ImageFormat format, const LoadOptions& options) const { + ImageFormat format, + const LoadOptions& options) const { LoadResult result; // Check for custom loader first @@ -318,10 +335,11 @@ LoadResult ImageLoader::loadFormat(const void* data, size_t size, case ImageFormat::PNG: case ImageFormat::BMP: case ImageFormat::TIFF: { - std::vector buffer(static_cast(data), - static_cast(data) + size); + std::vector buffer( + static_cast(data), + static_cast(data) + size); cv::Mat mat = cv::imdecode(buffer, cv::IMREAD_UNCHANGED); - + if (mat.empty()) { result.errorMessage = "Failed to decode image with OpenCV"; return result; @@ -342,13 +360,17 @@ LoadResult ImageLoader::loadFormat(const void* data, size_t size, unsigned char* pixels = stbi_load_from_memory( static_cast(data), static_cast(size), &width, &height, &channels, 0); - + if (!pixels) { - result.errorMessage = "Failed to decode image with stb_image: " + std::string(stbi_failure_reason()); + result.errorMessage = + "Failed to decode image with stb_image: " + + std::string(stbi_failure_reason()); return result; } - size_t imageSize = static_cast(width) * static_cast(height) * static_cast(channels); + size_t imageSize = static_cast(width) * + static_cast(height) * + static_cast(channels); result.imageData = blob(pixels, imageSize, height, width, channels); stbi_image_free(pixels); result.success = true; @@ -357,23 +379,29 @@ LoadResult ImageLoader::loadFormat(const void* data, size_t size, #endif default: - result.errorMessage = "Unsupported format for loading: " + formatDetector_->getFormatName(format); + result.errorMessage = "Unsupported format for loading: " + + formatDetector_->getFormatName(format); break; } return result; } -blob ImageLoader::applyPostProcessing(const blob& imageData, const LoadOptions& options) const { +blob ImageLoader::applyPostProcessing(const blob& imageData, + const LoadOptions& options) const { blob processed = imageData; // Apply resizing if requested if (options.targetWidth > 0 || options.targetHeight > 0) { - int newWidth = options.targetWidth > 0 ? options.targetWidth : processed.getWidth(); - int newHeight = options.targetHeight > 0 ? options.targetHeight : processed.getHeight(); - - if (options.preserveAspectRatio && options.targetWidth > 0 && options.targetHeight > 0) { - float aspectRatio = static_cast(processed.getWidth()) / processed.getHeight(); + int newWidth = options.targetWidth > 0 ? options.targetWidth + : processed.getWidth(); + int newHeight = options.targetHeight > 0 ? options.targetHeight + : processed.getHeight(); + + if (options.preserveAspectRatio && options.targetWidth > 0 && + options.targetHeight > 0) { + float aspectRatio = static_cast(processed.getWidth()) / + processed.getHeight(); if (newWidth / aspectRatio <= newHeight) { newHeight = static_cast(newWidth / aspectRatio); } else { @@ -391,13 +419,12 @@ blob ImageLoader::applyPostProcessing(const blob& imageData, const LoadOptions& std::unordered_map ImageLoader::extractMetadata( const void* data, size_t size, ImageFormat format) const { - std::unordered_map metadata; metadata["format"] = formatDetector_->getFormatName(format); metadata["size_bytes"] = std::to_string(size); - + // Format-specific metadata extraction could be added here - + return metadata; } @@ -411,18 +438,19 @@ blob quickLoadImage(const std::filesystem::path& filePath) { return result.success ? result.imageData : blob{}; } -std::vector quickLoadBatch(const std::vector& filePaths, - size_t maxConcurrency) { +std::vector quickLoadBatch( + const std::vector& filePaths, + size_t maxConcurrency) { auto loader = createImageLoader(); auto batchResult = loader->loadBatch(filePaths, {}, maxConcurrency); - + std::vector images; images.reserve(batchResult.results.size()); - + for (const auto& result : batchResult.results) { images.push_back(result.success ? result.imageData : blob{}); } - + return images; } diff --git a/atom/image/io/image_loader.hpp b/atom/image/io/image_loader.hpp index 6986b632..9b1f8332 100644 --- a/atom/image/io/image_loader.hpp +++ b/atom/image/io/image_loader.hpp @@ -13,15 +13,15 @@ * @version 1.0.0 */ -#include "../core/image_blob.hpp" -#include "format_detector.hpp" -#include -#include -#include #include #include #include +#include +#include #include +#include +#include "../core/image_blob.hpp" +#include "format_detector.hpp" namespace atom::image { @@ -29,17 +29,19 @@ namespace atom::image { * @brief Image loading options */ struct LoadOptions { - ImageFormat preferredFormat = ImageFormat::UNKNOWN; // Auto-detect if UNKNOWN - bool convertToRGB = false; // Convert to RGB color space - bool normalizePixels = false; // Normalize pixel values to [0,1] - int targetWidth = -1; // Resize to target width (-1 = no resize) - int targetHeight = -1; // Resize to target height (-1 = no resize) - bool preserveAspectRatio = true; // Preserve aspect ratio during resize - bool useMemoryMapping = false; // Use memory mapping for large files - size_t maxMemoryUsage = 1024 * 1024 * 1024; // Maximum memory usage (1GB) - bool enableCaching = true; // Enable result caching - std::string cacheKey; // Custom cache key - std::unordered_map customOptions; // Format-specific options + ImageFormat preferredFormat = + ImageFormat::UNKNOWN; // Auto-detect if UNKNOWN + bool convertToRGB = false; // Convert to RGB color space + bool normalizePixels = false; // Normalize pixel values to [0,1] + int targetWidth = -1; // Resize to target width (-1 = no resize) + int targetHeight = -1; // Resize to target height (-1 = no resize) + bool preserveAspectRatio = true; // Preserve aspect ratio during resize + bool useMemoryMapping = false; // Use memory mapping for large files + size_t maxMemoryUsage = 1024 * 1024 * 1024; // Maximum memory usage (1GB) + bool enableCaching = true; // Enable result caching + std::string cacheKey; // Custom cache key + std::unordered_map + customOptions; // Format-specific options // Default constructor LoadOptions() = default; @@ -48,7 +50,8 @@ struct LoadOptions { /** * @brief Loading progress callback */ -using ProgressCallback = std::function; +using ProgressCallback = + std::function; /** * @brief Loading result @@ -87,9 +90,9 @@ class ImageLoader { * @param progressCallback Optional progress callback * @return Loading result */ - virtual LoadResult loadFromFile(const std::filesystem::path& filePath, - const LoadOptions& options = {}, - ProgressCallback progressCallback = nullptr) const; + virtual LoadResult loadFromFile( + const std::filesystem::path& filePath, const LoadOptions& options = {}, + ProgressCallback progressCallback = nullptr) const; /** * @brief Load image from memory buffer @@ -99,9 +102,9 @@ class ImageLoader { * @param progressCallback Optional progress callback * @return Loading result */ - virtual LoadResult loadFromMemory(const void* data, size_t size, - const LoadOptions& options = {}, - ProgressCallback progressCallback = nullptr) const; + virtual LoadResult loadFromMemory( + const void* data, size_t size, const LoadOptions& options = {}, + ProgressCallback progressCallback = nullptr) const; /** * @brief Load image from URL (HTTP/HTTPS) @@ -110,9 +113,9 @@ class ImageLoader { * @param progressCallback Optional progress callback * @return Loading result */ - virtual LoadResult loadFromURL(const std::string& url, - const LoadOptions& options = {}, - ProgressCallback progressCallback = nullptr) const; + virtual LoadResult loadFromURL( + const std::string& url, const LoadOptions& options = {}, + ProgressCallback progressCallback = nullptr) const; /** * @brief Load multiple images in batch @@ -122,10 +125,10 @@ class ImageLoader { * @param progressCallback Optional progress callback * @return Batch loading result */ - virtual BatchLoadResult loadBatch(const std::vector& filePaths, - const LoadOptions& options = {}, - size_t maxConcurrency = 4, - ProgressCallback progressCallback = nullptr) const; + virtual BatchLoadResult loadBatch( + const std::vector& filePaths, + const LoadOptions& options = {}, size_t maxConcurrency = 4, + ProgressCallback progressCallback = nullptr) const; /** * @brief Load image asynchronously @@ -134,9 +137,9 @@ class ImageLoader { * @param progressCallback Optional progress callback * @return Future containing loading result */ - virtual std::future loadAsync(const std::filesystem::path& filePath, - const LoadOptions& options = {}, - ProgressCallback progressCallback = nullptr) const; + virtual std::future loadAsync( + const std::filesystem::path& filePath, const LoadOptions& options = {}, + ProgressCallback progressCallback = nullptr) const; /** * @brief Check if file can be loaded @@ -173,8 +176,10 @@ class ImageLoader { * @param format Image format * @param loader Custom loader function */ - virtual void registerCustomLoader(ImageFormat format, - std::function loader); + virtual void registerCustomLoader( + ImageFormat format, + std::function + loader); protected: /** @@ -186,7 +191,8 @@ class ImageLoader { * @return Loading result */ virtual LoadResult loadFormat(const void* data, size_t size, - ImageFormat format, const LoadOptions& options) const; + ImageFormat format, + const LoadOptions& options) const; /** * @brief Apply post-processing options @@ -194,7 +200,8 @@ class ImageLoader { * @param options Loading options * @return Processed image data */ - virtual blob applyPostProcessing(const blob& imageData, const LoadOptions& options) const; + virtual blob applyPostProcessing(const blob& imageData, + const LoadOptions& options) const; /** * @brief Extract metadata from image data @@ -208,13 +215,16 @@ class ImageLoader { private: std::unique_ptr formatDetector_; - std::unordered_map> customLoaders_; - + std::unordered_map< + ImageFormat, + std::function> + customLoaders_; + // Cache management mutable std::unordered_map imageCache_; mutable size_t cacheSize_ = 0; size_t maxCacheSize_ = 100 * 1024 * 1024; // 100MB default - + // Statistics mutable size_t cacheHits_ = 0; mutable size_t cacheMisses_ = 0; @@ -239,8 +249,9 @@ blob quickLoadImage(const std::filesystem::path& filePath); * @param maxConcurrency Maximum concurrent operations * @return Vector of loaded image blobs */ -std::vector quickLoadBatch(const std::vector& filePaths, - size_t maxConcurrency = 4); +std::vector quickLoadBatch( + const std::vector& filePaths, + size_t maxConcurrency = 4); } // namespace atom::image diff --git a/atom/image/io/image_saver.cpp b/atom/image/io/image_saver.cpp index 57296da3..2199144d 100644 --- a/atom/image/io/image_saver.cpp +++ b/atom/image/io/image_saver.cpp @@ -1,21 +1,23 @@ #include "image_saver.hpp" -#include -#include #include #include #include +#include #include #include +#include // Define error macros to avoid atom error system namespace pollution #define THROW_RUNTIME_ERROR(msg) throw std::runtime_error(msg) -#define THROW_FAIL_TO_OPEN_FILE(msg, path) throw std::runtime_error(std::string(msg) + std::string(path)) -#define THROW_FAIL_TO_WRITE_FILE(msg, path) throw std::runtime_error(std::string(msg) + std::string(path)) +#define THROW_FAIL_TO_OPEN_FILE(msg, path) \ + throw std::runtime_error(std::string(msg) + std::string(path)) +#define THROW_FAIL_TO_WRITE_FILE(msg, path) \ + throw std::runtime_error(std::string(msg) + std::string(path)) #define THROW_INVALID_ARGUMENT(msg) throw std::invalid_argument(msg) #ifdef ATOM_IMAGE_HAS_OPENCV -#include #include +#include #endif #ifdef ATOM_IMAGE_HAS_STB @@ -27,244 +29,273 @@ namespace atom::image { ImageSaver::ImageSaver() : formatDetector_(createFormatDetector()) {} SaveResult ImageSaver::saveToFile(const blob& imageData, - const std::filesystem::path& filePath, - const SaveOptions& options, - SaveProgressCallback progressCallback) const { + const std::filesystem::path& filePath, + const SaveOptions& options, + SaveProgressCallback progressCallback) const { auto startTime = std::chrono::high_resolution_clock::now(); SaveResult result; - + try { if (progressCallback) { progressCallback(0.0f, "Starting file save"); } - + // Validate input if (imageData.isEmpty()) { result.errorMessage = "Cannot save empty image data"; return result; } - + // Create directories if needed if (options.createDirectories) { std::filesystem::create_directories(filePath.parent_path()); } - + // Handle existing file backup if (std::filesystem::exists(filePath) && !options.overwriteExisting) { if (!createBackup(filePath, options.backupSuffix)) { - result.errorMessage = "Failed to create backup of existing file"; + result.errorMessage = + "Failed to create backup of existing file"; return result; } } - + // Determine target format ImageFormat targetFormat = options.targetFormat; if (targetFormat == ImageFormat::UNKNOWN) { std::string extension = filePath.extension().string(); - std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower); - - if (extension == ".jpg" || extension == ".jpeg") targetFormat = ImageFormat::JPEG; - else if (extension == ".png") targetFormat = ImageFormat::PNG; - else if (extension == ".bmp") targetFormat = ImageFormat::BMP; - else if (extension == ".tiff" || extension == ".tif") targetFormat = ImageFormat::TIFF; - else if (extension == ".tga") targetFormat = ImageFormat::TGA; - else if (extension == ".webp") targetFormat = ImageFormat::WEBP; - else if (extension == ".fits") targetFormat = ImageFormat::FITS; + std::transform(extension.begin(), extension.end(), + extension.begin(), ::tolower); + + if (extension == ".jpg" || extension == ".jpeg") + targetFormat = ImageFormat::JPEG; + else if (extension == ".png") + targetFormat = ImageFormat::PNG; + else if (extension == ".bmp") + targetFormat = ImageFormat::BMP; + else if (extension == ".tiff" || extension == ".tif") + targetFormat = ImageFormat::TIFF; + else if (extension == ".tga") + targetFormat = ImageFormat::TGA; + else if (extension == ".webp") + targetFormat = ImageFormat::WEBP; + else if (extension == ".fits") + targetFormat = ImageFormat::FITS; else { - result.errorMessage = "Unsupported file extension: " + extension; + result.errorMessage = + "Unsupported file extension: " + extension; return result; } } - + if (progressCallback) { progressCallback(0.2f, "Format determined"); } - + // Check if format is supported if (!canSave(targetFormat)) { - result.errorMessage = "Format not supported for saving: " + std::to_string(static_cast(targetFormat)); + result.errorMessage = + "Format not supported for saving: " + + std::to_string(static_cast(targetFormat)); return result; } - + // Apply preprocessing blob processedData = applyPreProcessing(imageData, options); - + if (progressCallback) { progressCallback(0.4f, "Preprocessing complete"); } - + // Encode image data - auto [encodedData, encodeResult] = saveFormat(processedData, targetFormat, options); + auto [encodedData, encodeResult] = + saveFormat(processedData, targetFormat, options); if (!encodeResult.success) { result = encodeResult; return result; } - + if (progressCallback) { progressCallback(0.7f, "Encoding complete"); } - + // Embed metadata if requested if (options.preserveMetadata && !options.customMetadata.empty()) { - encodedData = embedMetadata(encodedData, targetFormat, options.customMetadata); + encodedData = embedMetadata(encodedData, targetFormat, + options.customMetadata); } - + if (progressCallback) { progressCallback(0.8f, "Metadata embedded"); } - + // Write to file std::ofstream file(filePath, std::ios::binary); if (!file) { - THROW_FAIL_TO_OPEN_FILE("Cannot open file for writing: ", filePath.string()); + THROW_FAIL_TO_OPEN_FILE("Cannot open file for writing: ", + filePath.string()); } - file.write(reinterpret_cast(encodedData.data()), encodedData.size()); + file.write(reinterpret_cast(encodedData.data()), + encodedData.size()); if (!file) { - THROW_FAIL_TO_WRITE_FILE("Failed to write image data to file: ", filePath.string()); + THROW_FAIL_TO_WRITE_FILE("Failed to write image data to file: ", + filePath.string()); } - + if (progressCallback) { progressCallback(1.0f, "Save complete"); } - + // Calculate results auto endTime = std::chrono::high_resolution_clock::now(); result.savedPath = filePath; result.usedFormat = targetFormat; result.outputSize = encodedData.size(); result.success = true; - result.saveTime = std::chrono::duration_cast(endTime - startTime); - result.compressionRatio = static_cast(encodedData.size()) / static_cast(imageData.size()); - + result.saveTime = std::chrono::duration_cast( + endTime - startTime); + result.compressionRatio = static_cast(encodedData.size()) / + static_cast(imageData.size()); + } catch (const std::exception& e) { result.errorMessage = e.what(); result.success = false; } - + return result; } std::pair, SaveResult> ImageSaver::saveToMemory( - const blob& imageData, - ImageFormat format, - const SaveOptions& options, + const blob& imageData, ImageFormat format, const SaveOptions& options, SaveProgressCallback progressCallback) const { - SaveResult result; std::vector encodedData; - + try { if (progressCallback) { progressCallback(0.0f, "Starting memory save"); } - + // Validate input if (imageData.isEmpty()) { result.errorMessage = "Cannot save empty image data"; return {encodedData, result}; } - + // Check if format is supported if (!canSave(format)) { result.errorMessage = "Format not supported for saving"; return {encodedData, result}; } - + if (progressCallback) { progressCallback(0.2f, "Validation complete"); } - + // Apply preprocessing blob processedData = applyPreProcessing(imageData, options); - + if (progressCallback) { progressCallback(0.4f, "Preprocessing complete"); } - + // Encode image data auto [data, encodeResult] = saveFormat(processedData, format, options); if (!encodeResult.success) { return {encodedData, encodeResult}; } - + encodedData = std::move(data); - + if (progressCallback) { progressCallback(0.8f, "Encoding complete"); } - + // Embed metadata if requested if (options.preserveMetadata && !options.customMetadata.empty()) { - encodedData = embedMetadata(encodedData, format, options.customMetadata); + encodedData = + embedMetadata(encodedData, format, options.customMetadata); } - + if (progressCallback) { progressCallback(1.0f, "Save complete"); } - + result.usedFormat = format; result.outputSize = encodedData.size(); result.success = true; - result.compressionRatio = static_cast(encodedData.size()) / static_cast(imageData.size()); - + result.compressionRatio = static_cast(encodedData.size()) / + static_cast(imageData.size()); + } catch (const std::exception& e) { result.errorMessage = e.what(); result.success = false; } - + return {encodedData, result}; } -BatchSaveResult ImageSaver::saveBatch(const std::vector& imageData, - const std::vector& filePaths, - const SaveOptions& options, - size_t maxConcurrency, - SaveProgressCallback progressCallback) const { +BatchSaveResult ImageSaver::saveBatch( + const std::vector& imageData, + const std::vector& filePaths, + const SaveOptions& options, size_t maxConcurrency, + SaveProgressCallback progressCallback) const { BatchSaveResult batchResult; auto startTime = std::chrono::high_resolution_clock::now(); - + if (imageData.size() != filePaths.size()) { - THROW_INVALID_ARGUMENT("Image data and file paths vectors must have the same size"); + THROW_INVALID_ARGUMENT( + "Image data and file paths vectors must have the same size"); } - + if (imageData.empty()) { return batchResult; } - + // Limit concurrency to reasonable bounds - maxConcurrency = (maxConcurrency < std::thread::hardware_concurrency()) ? maxConcurrency : std::thread::hardware_concurrency(); + maxConcurrency = (maxConcurrency < std::thread::hardware_concurrency()) + ? maxConcurrency + : std::thread::hardware_concurrency(); maxConcurrency = (maxConcurrency > size_t(1)) ? maxConcurrency : size_t(1); - + batchResult.results.resize(imageData.size()); std::atomic completedCount{0}; - + // Process in batches std::vector> futures; futures.reserve(maxConcurrency); - + for (size_t i = 0; i < imageData.size(); i += maxConcurrency) { size_t batchEnd = std::min(i + maxConcurrency, imageData.size()); - + for (size_t j = i; j < batchEnd; ++j) { - futures.emplace_back(std::async(std::launch::async, [this, &imageData, &filePaths, &options, &batchResult, &completedCount, &progressCallback, j]() { - batchResult.results[j] = saveToFile(imageData[j], filePaths[j], options); - - size_t completed = ++completedCount; - if (progressCallback) { - float progress = static_cast(completed) / static_cast(imageData.size()); - progressCallback(progress, "Processed " + std::to_string(completed) + "/" + std::to_string(imageData.size())); - } - })); + futures.emplace_back(std::async( + std::launch::async, + [this, &imageData, &filePaths, &options, &batchResult, + &completedCount, &progressCallback, j]() { + batchResult.results[j] = + saveToFile(imageData[j], filePaths[j], options); + + size_t completed = ++completedCount; + if (progressCallback) { + float progress = static_cast(completed) / + static_cast(imageData.size()); + progressCallback( + progress, "Processed " + std::to_string(completed) + + "/" + + std::to_string(imageData.size())); + } + })); } - + // Wait for current batch to complete for (auto& future : futures) { future.wait(); } futures.clear(); } - + // Calculate batch statistics for (const auto& result : batchResult.results) { if (result.success) { @@ -274,18 +305,20 @@ BatchSaveResult ImageSaver::saveBatch(const std::vector& imageData, batchResult.failureCount++; } } - + auto endTime = std::chrono::high_resolution_clock::now(); - batchResult.totalTime = std::chrono::duration_cast(endTime - startTime); - + batchResult.totalTime = + std::chrono::duration_cast(endTime - + startTime); + return batchResult; } -std::future ImageSaver::saveAsync(const blob& imageData, - const std::filesystem::path& filePath, - const SaveOptions& options, - SaveProgressCallback progressCallback) const { - return std::async(std::launch::async, [this, imageData, filePath, options, progressCallback]() { +std::future ImageSaver::saveAsync( + const blob& imageData, const std::filesystem::path& filePath, + const SaveOptions& options, SaveProgressCallback progressCallback) const { + return std::async(std::launch::async, [this, imageData, filePath, options, + progressCallback]() { return saveToFile(imageData, filePath, options, progressCallback); }); } @@ -310,12 +343,8 @@ bool ImageSaver::canSave(ImageFormat format) const { } std::vector ImageSaver::getSupportedFormats() const { - std::vector formats = { - ImageFormat::JPEG, - ImageFormat::PNG, - ImageFormat::BMP, - ImageFormat::TGA - }; + std::vector formats = {ImageFormat::JPEG, ImageFormat::PNG, + ImageFormat::BMP, ImageFormat::TGA}; #ifdef ATOM_IMAGE_HAS_OPENCV formats.push_back(ImageFormat::TIFF); @@ -334,7 +363,8 @@ std::vector ImageSaver::getSupportedFormats() const { return formats; } -ImageFormat ImageSaver::getOptimalFormat(const blob& imageData, bool preferLossless) const { +ImageFormat ImageSaver::getOptimalFormat(const blob& imageData, + bool preferLossless) const { if (imageData.isEmpty()) { return ImageFormat::UNKNOWN; } @@ -353,14 +383,13 @@ ImageFormat ImageSaver::getOptimalFormat(const blob& imageData, bool preferLossl if (hasAlpha) { return ImageFormat::PNG; // JPEG doesn't support alpha } else { - return ImageFormat::JPEG; // JPEG is good for photos + return ImageFormat::JPEG; // JPEG is good for photos } } } -size_t ImageSaver::estimateOutputSize(const blob& imageData, - ImageFormat format, - const SaveOptions& options) const { +size_t ImageSaver::estimateOutputSize(const blob& imageData, ImageFormat format, + const SaveOptions& options) const { if (imageData.isEmpty()) { return 0; } @@ -370,7 +399,8 @@ size_t ImageSaver::estimateOutputSize(const blob& imageData, switch (format) { case ImageFormat::JPEG: { // JPEG compression ratio depends on quality - float compressionRatio = 0.1f + (static_cast(options.quality) / 100.0f) * 0.4f; + float compressionRatio = + 0.1f + (static_cast(options.quality) / 100.0f) * 0.4f; return static_cast(inputSize * compressionRatio); } case ImageFormat::PNG: { @@ -379,29 +409,30 @@ size_t ImageSaver::estimateOutputSize(const blob& imageData, } case ImageFormat::BMP: { // BMP is typically uncompressed - return inputSize + 54; // BMP header size + return inputSize + 54; // BMP header size } case ImageFormat::TIFF: { // TIFF can be compressed or uncompressed if (options.compression == CompressionType::NONE) { - return inputSize + 1024; // Estimate header overhead + return inputSize + 1024; // Estimate header overhead } else { return static_cast(inputSize * 0.8f); } } default: - return inputSize; // Conservative estimate + return inputSize; // Conservative estimate } } -void ImageSaver::registerCustomSaver(ImageFormat format, - std::function saver) { +void ImageSaver::registerCustomSaver( + ImageFormat format, + std::function saver) { customSavers_[format] = std::move(saver); } std::pair, SaveResult> ImageSaver::saveFormat( - const blob& imageData, ImageFormat format, const SaveOptions& options) const { - + const blob& imageData, ImageFormat format, + const SaveOptions& options) const { SaveResult result; std::vector encodedData; @@ -411,8 +442,9 @@ std::pair, SaveResult> ImageSaver::saveFormat( if (customIt != customSavers_.end()) { result = customIt->second(imageData, options); if (result.success) { - // Custom savers should return data in result, but we need it as vector - // This is a simplified approach - in practice, custom savers would need to return data + // Custom savers should return data in result, but we need it as + // vector This is a simplified approach - in practice, custom + // savers would need to return data encodedData.resize(result.outputSize); } return {encodedData, result}; @@ -438,7 +470,8 @@ std::pair, SaveResult> ImageSaver::saveFormat( break; } case ImageFormat::PNG: { - params = {cv::IMWRITE_PNG_COMPRESSION, options.compressionLevel}; + params = {cv::IMWRITE_PNG_COMPRESSION, + options.compressionLevel}; cv::imencode(".png", mat, encodedData, params); break; } @@ -488,7 +521,8 @@ std::pair, SaveResult> ImageSaver::saveFormat( return {encodedData, result}; } -blob ImageSaver::applyPreProcessing(const blob& imageData, const SaveOptions& options) const { +blob ImageSaver::applyPreProcessing(const blob& imageData, + const SaveOptions& options) const { blob processedData = imageData; try { @@ -498,7 +532,8 @@ blob ImageSaver::applyPreProcessing(const blob& imageData, const SaveOptions& op // For now, just return the original data } - // Additional preprocessing could be added here based on format-specific options + // Additional preprocessing could be added here based on format-specific + // options } catch (const std::exception&) { // If preprocessing fails, return original data @@ -508,9 +543,9 @@ blob ImageSaver::applyPreProcessing(const blob& imageData, const SaveOptions& op return processedData; } -std::vector ImageSaver::embedMetadata(const std::vector& encodedData, - ImageFormat format, - const std::unordered_map& metadata) const { +std::vector ImageSaver::embedMetadata( + const std::vector& encodedData, ImageFormat format, + const std::unordered_map& metadata) const { // For now, return the original data // In a full implementation, this would embed metadata based on the format // JPEG: EXIF data @@ -522,7 +557,8 @@ std::vector ImageSaver::embedMetadata(const std::vector& encod return encodedData; } - // Placeholder implementation - would need format-specific metadata embedding + // Placeholder implementation - would need format-specific metadata + // embedding switch (format) { case ImageFormat::JPEG: // Would embed EXIF metadata @@ -541,16 +577,19 @@ std::vector ImageSaver::embedMetadata(const std::vector& encod return encodedData; } -bool ImageSaver::createBackup(const std::filesystem::path& filePath, const std::string& backupSuffix) const { +bool ImageSaver::createBackup(const std::filesystem::path& filePath, + const std::string& backupSuffix) const { try { if (!std::filesystem::exists(filePath)) { - return true; // No file to backup + return true; // No file to backup } std::filesystem::path backupPath = filePath; backupPath += backupSuffix; - std::filesystem::copy_file(filePath, backupPath, std::filesystem::copy_options::overwrite_existing); + std::filesystem::copy_file( + filePath, backupPath, + std::filesystem::copy_options::overwrite_existing); return true; } catch (const std::exception&) { @@ -563,7 +602,8 @@ std::unique_ptr createImageSaver() { return std::make_unique(); } -bool quickSaveImage(const blob& imageData, const std::filesystem::path& filePath, int quality) { +bool quickSaveImage(const blob& imageData, + const std::filesystem::path& filePath, int quality) { SaveOptions options; options.quality = quality; @@ -573,14 +613,14 @@ bool quickSaveImage(const blob& imageData, const std::filesystem::path& filePath } size_t quickSaveBatch(const std::vector& imageData, - const std::vector& filePaths, - int quality, - size_t maxConcurrency) { + const std::vector& filePaths, + int quality, size_t maxConcurrency) { SaveOptions options; options.quality = quality; auto saver = createImageSaver(); - auto result = saver->saveBatch(imageData, filePaths, options, maxConcurrency); + auto result = + saver->saveBatch(imageData, filePaths, options, maxConcurrency); return result.successCount; } diff --git a/atom/image/io/image_saver.hpp b/atom/image/io/image_saver.hpp index cf2a219e..d28b146d 100644 --- a/atom/image/io/image_saver.hpp +++ b/atom/image/io/image_saver.hpp @@ -14,15 +14,15 @@ * @version 1.0.0 */ -#include "../core/image_blob.hpp" -#include "format_detector.hpp" -#include -#include -#include #include #include #include +#include +#include #include +#include +#include "../core/image_blob.hpp" +#include "format_detector.hpp" namespace atom::image { @@ -30,29 +30,32 @@ namespace atom::image { * @brief Image compression types */ enum class CompressionType { - NONE, // No compression - LOSSLESS, // Lossless compression - LOSSY, // Lossy compression - ADAPTIVE, // Adaptive compression based on content - CUSTOM // Custom compression settings + NONE, // No compression + LOSSLESS, // Lossless compression + LOSSY, // Lossy compression + ADAPTIVE, // Adaptive compression based on content + CUSTOM // Custom compression settings }; /** * @brief Image saving options */ struct SaveOptions { - ImageFormat targetFormat = ImageFormat::UNKNOWN; // Auto-detect from extension if UNKNOWN + ImageFormat targetFormat = + ImageFormat::UNKNOWN; // Auto-detect from extension if UNKNOWN CompressionType compression = CompressionType::ADAPTIVE; - int quality = 95; // Quality for lossy formats (0-100) - bool preserveMetadata = true; // Preserve original metadata - bool optimizeSize = false; // Optimize for file size - bool progressiveEncoding = false; // Use progressive encoding (JPEG) - int compressionLevel = 6; // Compression level (0-9) - std::unordered_map customMetadata; // Additional metadata - std::unordered_map formatOptions; // Format-specific options - bool overwriteExisting = true; // Overwrite existing files - bool createDirectories = true; // Create directories if needed - std::string backupSuffix = ".bak"; // Backup suffix for existing files + int quality = 95; // Quality for lossy formats (0-100) + bool preserveMetadata = true; // Preserve original metadata + bool optimizeSize = false; // Optimize for file size + bool progressiveEncoding = false; // Use progressive encoding (JPEG) + int compressionLevel = 6; // Compression level (0-9) + std::unordered_map + customMetadata; // Additional metadata + std::unordered_map + formatOptions; // Format-specific options + bool overwriteExisting = true; // Overwrite existing files + bool createDirectories = true; // Create directories if needed + std::string backupSuffix = ".bak"; // Backup suffix for existing files // Default constructor SaveOptions() = default; @@ -61,7 +64,8 @@ struct SaveOptions { /** * @brief Saving progress callback */ -using SaveProgressCallback = std::function; +using SaveProgressCallback = + std::function; /** * @brief Saving result @@ -103,10 +107,10 @@ class ImageSaver { * @param progressCallback Optional progress callback * @return Saving result */ - virtual SaveResult saveToFile(const blob& imageData, - const std::filesystem::path& filePath, - const SaveOptions& options = {}, - SaveProgressCallback progressCallback = nullptr) const; + virtual SaveResult saveToFile( + const blob& imageData, const std::filesystem::path& filePath, + const SaveOptions& options = {}, + SaveProgressCallback progressCallback = nullptr) const; /** * @brief Save image to memory buffer @@ -117,8 +121,7 @@ class ImageSaver { * @return Encoded image data and result */ virtual std::pair, SaveResult> saveToMemory( - const blob& imageData, - ImageFormat format, + const blob& imageData, ImageFormat format, const SaveOptions& options = {}, SaveProgressCallback progressCallback = nullptr) const; @@ -131,11 +134,11 @@ class ImageSaver { * @param progressCallback Optional progress callback * @return Batch saving result */ - virtual BatchSaveResult saveBatch(const std::vector& imageData, - const std::vector& filePaths, - const SaveOptions& options = {}, - size_t maxConcurrency = 4, - SaveProgressCallback progressCallback = nullptr) const; + virtual BatchSaveResult saveBatch( + const std::vector& imageData, + const std::vector& filePaths, + const SaveOptions& options = {}, size_t maxConcurrency = 4, + SaveProgressCallback progressCallback = nullptr) const; /** * @brief Save image asynchronously @@ -145,10 +148,10 @@ class ImageSaver { * @param progressCallback Optional progress callback * @return Future containing saving result */ - virtual std::future saveAsync(const blob& imageData, - const std::filesystem::path& filePath, - const SaveOptions& options = {}, - SaveProgressCallback progressCallback = nullptr) const; + virtual std::future saveAsync( + const blob& imageData, const std::filesystem::path& filePath, + const SaveOptions& options = {}, + SaveProgressCallback progressCallback = nullptr) const; /** * @brief Check if format is supported for saving @@ -169,7 +172,8 @@ class ImageSaver { * @param preferLossless Prefer lossless formats * @return Recommended format */ - virtual ImageFormat getOptimalFormat(const blob& imageData, bool preferLossless = false) const; + virtual ImageFormat getOptimalFormat(const blob& imageData, + bool preferLossless = false) const; /** * @brief Estimate output size for format and options @@ -178,17 +182,17 @@ class ImageSaver { * @param options Saving options * @return Estimated output size in bytes */ - virtual size_t estimateOutputSize(const blob& imageData, - ImageFormat format, - const SaveOptions& options) const; + virtual size_t estimateOutputSize(const blob& imageData, ImageFormat format, + const SaveOptions& options) const; /** * @brief Register custom format saver * @param format Image format * @param saver Custom saver function */ - virtual void registerCustomSaver(ImageFormat format, - std::function saver); + virtual void registerCustomSaver( + ImageFormat format, + std::function saver); protected: /** @@ -199,7 +203,8 @@ class ImageSaver { * @return Encoded data and result */ virtual std::pair, SaveResult> saveFormat( - const blob& imageData, ImageFormat format, const SaveOptions& options) const; + const blob& imageData, ImageFormat format, + const SaveOptions& options) const; /** * @brief Apply pre-processing before saving @@ -207,7 +212,8 @@ class ImageSaver { * @param options Saving options * @return Processed image data */ - virtual blob applyPreProcessing(const blob& imageData, const SaveOptions& options) const; + virtual blob applyPreProcessing(const blob& imageData, + const SaveOptions& options) const; /** * @brief Write metadata to encoded data @@ -216,9 +222,9 @@ class ImageSaver { * @param metadata Metadata to write * @return Data with embedded metadata */ - virtual std::vector embedMetadata(const std::vector& encodedData, - ImageFormat format, - const std::unordered_map& metadata) const; + virtual std::vector embedMetadata( + const std::vector& encodedData, ImageFormat format, + const std::unordered_map& metadata) const; /** * @brief Create backup of existing file @@ -226,11 +232,14 @@ class ImageSaver { * @param backupSuffix Backup suffix * @return Success status */ - virtual bool createBackup(const std::filesystem::path& filePath, const std::string& backupSuffix) const; + virtual bool createBackup(const std::filesystem::path& filePath, + const std::string& backupSuffix) const; private: std::unique_ptr formatDetector_; - std::unordered_map> customSavers_; + std::unordered_map< + ImageFormat, std::function> + customSavers_; }; /** @@ -246,7 +255,8 @@ std::unique_ptr createImageSaver(); * @param quality Quality for lossy formats (0-100) * @return Success status */ -bool quickSaveImage(const blob& imageData, const std::filesystem::path& filePath, int quality = 95); +bool quickSaveImage(const blob& imageData, + const std::filesystem::path& filePath, int quality = 95); /** * @brief Quick batch image saving @@ -257,9 +267,8 @@ bool quickSaveImage(const blob& imageData, const std::filesystem::path& filePath * @return Number of successfully saved images */ size_t quickSaveBatch(const std::vector& imageData, - const std::vector& filePaths, - int quality = 95, - size_t maxConcurrency = 4); + const std::vector& filePaths, + int quality = 95, size_t maxConcurrency = 4); } // namespace atom::image diff --git a/atom/image/metadata/README.md b/atom/image/metadata/README.md index d26be958..ce20a592 100644 --- a/atom/image/metadata/README.md +++ b/atom/image/metadata/README.md @@ -62,6 +62,7 @@ exif.writeToFile("photo_modified.jpg"); #### Supported EXIF Tags **Basic Information** + - Camera make and model - Software used - Date and time @@ -69,6 +70,7 @@ exif.writeToFile("photo_modified.jpg"); - Color space **Camera Settings** + - Aperture (F-number) - Shutter speed - ISO sensitivity @@ -79,6 +81,7 @@ exif.writeToFile("photo_modified.jpg"); - Metering mode **GPS Data** + - Latitude and longitude - Altitude - Direction @@ -86,6 +89,7 @@ exif.writeToFile("photo_modified.jpg"); - Timestamp **Advanced Tags** + - Lens information - Color profile - Thumbnail data @@ -238,12 +242,14 @@ auto exif = cache.get("photo1.jpg"); ## Format Support ### EXIF-Compatible Formats + - **JPEG**: Full EXIF support - **TIFF**: Complete metadata support - **RAW**: Camera-specific metadata - **HEIF**: Modern EXIF support ### Limited Support + - **PNG**: Text chunks for basic metadata - **WebP**: XMP metadata support - **BMP**: No standard metadata support @@ -282,10 +288,12 @@ exif.setDateTime(timestamp, "en_US"); ## Dependencies ### Required + - atom-error: Error handling framework - atom-image-core: Core image functionality ### Optional + - libexif: Enhanced EXIF support - exiv2: Advanced metadata library - libiptc: IPTC metadata support diff --git a/atom/image/metadata/exif.cpp b/atom/image/metadata/exif.cpp index f13cff10..e167caa9 100644 --- a/atom/image/metadata/exif.cpp +++ b/atom/image/metadata/exif.cpp @@ -51,8 +51,8 @@ auto ExifParser::readUint32Le(const std::byte* data) -> uint32_t { (std::to_integer(data[3]) << BYTE_SHIFT_24); } -auto ExifParser::parseRational(const std::byte* data, bool isLittleEndian) - -> double { +auto ExifParser::parseRational(const std::byte* data, + bool isLittleEndian) -> double { uint32_t numerator = isLittleEndian ? readUint32Le(data) : readUint32Be(data); uint32_t denominator = @@ -61,8 +61,8 @@ auto ExifParser::parseRational(const std::byte* data, bool isLittleEndian) : static_cast(numerator) / denominator; } -auto ExifParser::parseGPSCoordinate(const std::byte* data, bool isLittleEndian) - -> std::string { +auto ExifParser::parseGPSCoordinate(const std::byte* data, + bool isLittleEndian) -> std::string { double degrees = parseRational(data, isLittleEndian); double minutes = parseRational(data + RATIONAL_SIZE, isLittleEndian); double seconds = parseRational(data + 2 * RATIONAL_SIZE, isLittleEndian); @@ -71,8 +71,8 @@ auto ExifParser::parseGPSCoordinate(const std::byte* data, bool isLittleEndian) } auto ExifParser::parseIFD(const std::byte* data, bool isLittleEndian, - const std::byte* tiffStart, size_t bufferSize) - -> bool { + const std::byte* tiffStart, + size_t bufferSize) -> bool { uint16_t entryCount = isLittleEndian ? readUint16Le(data) : readUint16Be(data); data += 2; @@ -166,8 +166,8 @@ auto ExifParser::parseIFD(const std::byte* data, bool isLittleEndian, return true; } -auto ExifParser::parseColorSpace(const std::byte* data, bool isLittleEndian) - -> std::string { +auto ExifParser::parseColorSpace(const std::byte* data, + bool isLittleEndian) -> std::string { uint16_t colorSpace = isLittleEndian ? readUint16Le(data) : readUint16Be(data); switch (colorSpace) { @@ -180,8 +180,8 @@ auto ExifParser::parseColorSpace(const std::byte* data, bool isLittleEndian) } } -auto ExifParser::parseOrientation(const std::byte* data, bool isLittleEndian) - -> std::string { +auto ExifParser::parseOrientation(const std::byte* data, + bool isLittleEndian) -> std::string { uint16_t orientation = isLittleEndian ? readUint16Le(data) : readUint16Be(data); switch (orientation) { diff --git a/atom/image/metadata/exif.hpp b/atom/image/metadata/exif.hpp index dd51c779..faf10ee9 100644 --- a/atom/image/metadata/exif.hpp +++ b/atom/image/metadata/exif.hpp @@ -152,8 +152,8 @@ class ExifParser { * format. * @return The parsed GPS coordinate as a string. */ - auto parseGPSCoordinate(const std::byte* data, bool isLittleEndian) - -> std::string; + auto parseGPSCoordinate(const std::byte* data, + bool isLittleEndian) -> std::string; /** * @brief Parses a rational number from the EXIF data. @@ -209,10 +209,10 @@ class ExifParser { std::string m_filename; ExifData m_exifData; - auto parseColorSpace(const std::byte* data, bool isLittleEndian) - -> std::string; - auto parseOrientation(const std::byte* data, bool isLittleEndian) - -> std::string; + auto parseColorSpace(const std::byte* data, + bool isLittleEndian) -> std::string; + auto parseOrientation(const std::byte* data, + bool isLittleEndian) -> std::string; }; } // namespace atom::image diff --git a/atom/image/processing/README.md b/atom/image/processing/README.md index 5aa76808..cc1febdf 100644 --- a/atom/image/processing/README.md +++ b/atom/image/processing/README.md @@ -200,10 +200,12 @@ try { ## Dependencies ### Required + - atom-error: Error handling framework - atom-image-core: Core image functionality ### Optional + - OpenCV: Advanced computer vision operations - Tesseract: OCR text recognition - Leptonica: Image preprocessing for OCR diff --git a/atom/image/processing/computer_vision.cpp b/atom/image/processing/computer_vision.cpp index d57ce61a..cace05c8 100644 --- a/atom/image/processing/computer_vision.cpp +++ b/atom/image/processing/computer_vision.cpp @@ -1,7 +1,7 @@ -#include #include #include #include +#include #include #include "../core/image_blob.hpp" @@ -13,22 +13,21 @@ #include "computer_vision.hpp" #ifdef ATOM_IMAGE_HAS_OPENCV -#include +#include #include #include #include -#include -#include +#include #include +#include using namespace cv; #endif namespace atom::image { -std::vector ComputerVision::detectFeatures(const blob& input, - FeatureDetectorType detectorType, - int maxFeatures, - double qualityLevel) const { +std::vector ComputerVision::detectFeatures( + const blob& input, FeatureDetectorType detectorType, int maxFeatures, + double qualityLevel) const { if (input.isEmpty()) { return {}; } @@ -36,41 +35,46 @@ std::vector ComputerVision::detectFeatures(const blob& input, #ifdef ATOM_IMAGE_HAS_OPENCV cv::Mat src = input.to_mat(); cv::Mat gray; - + // Convert to grayscale if needed if (src.channels() > 1) { cv::cvtColor(src, gray, cv::COLOR_BGR2GRAY); } else { gray = src; } - + std::vector keypoints; cv::Mat descriptors; - + switch (detectorType) { case FeatureDetectorType::ORB: { auto detector = cv::ORB::create(maxFeatures); - detector->detectAndCompute(gray, cv::noArray(), keypoints, descriptors); + detector->detectAndCompute(gray, cv::noArray(), keypoints, + descriptors); break; } case FeatureDetectorType::SIFT: { auto detector = cv::SIFT::create(maxFeatures); - detector->detectAndCompute(gray, cv::noArray(), keypoints, descriptors); + detector->detectAndCompute(gray, cv::noArray(), keypoints, + descriptors); break; } case FeatureDetectorType::SURF: { // Note: SURF is in opencv_contrib - THROW_RUNTIME_ERROR("SURF detector requires OpenCV contrib modules"); + THROW_RUNTIME_ERROR( + "SURF detector requires OpenCV contrib modules"); break; } case FeatureDetectorType::AKAZE: { auto detector = cv::AKAZE::create(); - detector->detectAndCompute(gray, cv::noArray(), keypoints, descriptors); + detector->detectAndCompute(gray, cv::noArray(), keypoints, + descriptors); break; } case FeatureDetectorType::BRISK: { auto detector = cv::BRISK::create(); - detector->detectAndCompute(gray, cv::noArray(), keypoints, descriptors); + detector->detectAndCompute(gray, cv::noArray(), keypoints, + descriptors); break; } case FeatureDetectorType::FAST: { @@ -82,17 +86,18 @@ std::vector ComputerVision::detectFeatures(const blob& input, // Harris corner detection cv::Mat corners; cv::cornerHarris(gray, corners, 2, 3, 0.04); - + // Find local maxima cv::Mat dilated; cv::dilate(corners, dilated, cv::Mat()); - + for (int y = 0; y < corners.rows; ++y) { for (int x = 0; x < corners.cols; ++x) { - if (corners.at(y, x) > qualityLevel * 255 && + if (corners.at(y, x) > qualityLevel * 255 && corners.at(y, x) == dilated.at(y, x)) { keypoints.emplace_back(cv::Point2f(x, y), 1.0f); - if (keypoints.size() >= static_cast(maxFeatures)) { + if (keypoints.size() >= + static_cast(maxFeatures)) { break; } } @@ -105,8 +110,9 @@ std::vector ComputerVision::detectFeatures(const blob& input, } case FeatureDetectorType::GFTT: { std::vector corners; - cv::goodFeaturesToTrack(gray, corners, maxFeatures, qualityLevel, 10.0); - + cv::goodFeaturesToTrack(gray, corners, maxFeatures, qualityLevel, + 10.0); + for (const auto& corner : corners) { keypoints.emplace_back(corner, 1.0f); } @@ -115,11 +121,11 @@ std::vector ComputerVision::detectFeatures(const blob& input, default: THROW_RUNTIME_ERROR("Unsupported feature detector type"); } - + // Convert OpenCV keypoints to our format std::vector result; result.reserve(keypoints.size()); - + for (size_t i = 0; i < keypoints.size(); ++i) { const auto& kp = keypoints[i]; Keypoint point; @@ -129,17 +135,18 @@ std::vector ComputerVision::detectFeatures(const blob& input, point.angle = kp.angle; point.response = kp.response; point.octave = kp.octave; - + // Copy descriptor if available if (!descriptors.empty() && i < static_cast(descriptors.rows)) { cv::Mat desc = descriptors.row(static_cast(i)); point.descriptor.resize(desc.cols); - std::memcpy(point.descriptor.data(), desc.data, desc.cols * sizeof(float)); + std::memcpy(point.descriptor.data(), desc.data, + desc.cols * sizeof(float)); } - + result.push_back(point); } - + return result; #else THROW_RUNTIME_ERROR("OpenCV required for feature detection"); @@ -148,8 +155,7 @@ std::vector ComputerVision::detectFeatures(const blob& input, std::vector> ComputerVision::matchFeatures( const std::vector& keypoints1, - const std::vector& keypoints2, - const std::string& matchingMethod, + const std::vector& keypoints2, const std::string& matchingMethod, double distanceThreshold) const { if (keypoints1.empty() || keypoints2.empty()) { return {}; @@ -161,15 +167,19 @@ std::vector> ComputerVision::matchFeatures( } size_t descSize = keypoints1[0].descriptor.size(); - Mat desc1(static_cast(keypoints1.size()), static_cast(descSize), CV_32F); - Mat desc2(static_cast(keypoints2.size()), static_cast(descSize), CV_32F); + Mat desc1(static_cast(keypoints1.size()), static_cast(descSize), + CV_32F); + Mat desc2(static_cast(keypoints2.size()), static_cast(descSize), + CV_32F); for (size_t i = 0; i < keypoints1.size(); ++i) { - std::copy(keypoints1[i].descriptor.begin(), keypoints1[i].descriptor.end(), + std::copy(keypoints1[i].descriptor.begin(), + keypoints1[i].descriptor.end(), desc1.ptr(static_cast(i))); } for (size_t i = 0; i < keypoints2.size(); ++i) { - std::copy(keypoints2[i].descriptor.begin(), keypoints2[i].descriptor.end(), + std::copy(keypoints2[i].descriptor.begin(), + keypoints2[i].descriptor.end(), desc2.ptr(static_cast(i))); } @@ -190,7 +200,8 @@ std::vector> ComputerVision::matchFeatures( if (knnMatches[i].size() == 2) { const DMatch& m1 = knnMatches[i][0]; const DMatch& m2 = knnMatches[i][1]; - if (m1.distance < distanceThreshold * m2.distance && m1.distance < 0.7 * 256) { // Normalize distance + if (m1.distance < distanceThreshold * m2.distance && + m1.distance < 0.7 * 256) { // Normalize distance matches.emplace_back(static_cast(i), m1.trainIdx); } } else if (knnMatches[i].size() == 1) { @@ -204,11 +215,9 @@ std::vector> ComputerVision::matchFeatures( return matches; } -std::vector ComputerVision::detectObjects(const blob& input, - ObjectDetectionModel model, - double confidenceThreshold, - double nmsThreshold, - const std::string& modelPath) const { +std::vector ComputerVision::detectObjects( + const blob& input, ObjectDetectionModel model, double confidenceThreshold, + double nmsThreshold, const std::string& modelPath) const { if (input.isEmpty()) { return {}; } @@ -216,66 +225,75 @@ std::vector ComputerVision::detectObjects(const blob& input, #ifdef ATOM_IMAGE_HAS_OPENCV cv::Mat src = input.to_mat(); std::vector detections; - + switch (model) { case ObjectDetectionModel::YOLO_V5: case ObjectDetectionModel::YOLO_V8: { if (modelPath.empty()) { THROW_RUNTIME_ERROR("Model path required for YOLO detection"); } - + // Load YOLO model cv::dnn::Net net = cv::dnn::readNet(modelPath); - + // Prepare input blob cv::Mat blob; - cv::dnn::blobFromImage(src, blob, 1.0/255.0, cv::Size(640, 640), cv::Scalar(0,0,0), true, false); + cv::dnn::blobFromImage(src, blob, 1.0 / 255.0, cv::Size(640, 640), + cv::Scalar(0, 0, 0), true, false); net.setInput(blob); - + // Run inference std::vector outputs; net.forward(outputs, net.getUnconnectedOutLayersNames()); - + // Process outputs (simplified) for (const auto& output : outputs) { for (int i = 0; i < output.rows; ++i) { const float* data = output.ptr(i); float confidence = data[4]; - + if (confidence > confidenceThreshold) { Detection detection; - detection.x = static_cast((data[0] - data[2]/2.0) * src.cols); - detection.y = static_cast((data[1] - data[3]/2.0) * src.rows); - detection.width = static_cast(data[2] * src.cols); - detection.height = static_cast(data[3] * src.rows); + detection.x = static_cast( + (data[0] - data[2] / 2.0) * src.cols); + detection.y = static_cast( + (data[1] - data[3] / 2.0) * src.rows); + detection.width = + static_cast(data[2] * src.cols); + detection.height = + static_cast(data[3] * src.rows); detection.confidence = confidence; - detection.classId = static_cast(std::max_element(data + 5, data + output.cols) - (data + 5)); - detection.className = "object_" + std::to_string(detection.classId); - + detection.classId = static_cast( + std::max_element(data + 5, data + output.cols) - + (data + 5)); + detection.className = + "object_" + std::to_string(detection.classId); + detections.push_back(detection); } } } - + // Apply Non-Maximum Suppression std::vector boxes; std::vector confidences; std::vector classIds; - + for (const auto& det : detections) { boxes.emplace_back(det.x, det.y, det.width, det.height); confidences.push_back(det.confidence); classIds.push_back(det.classId); } - + std::vector indices; - cv::dnn::NMSBoxes(boxes, confidences, confidenceThreshold, nmsThreshold, indices); - + cv::dnn::NMSBoxes(boxes, confidences, confidenceThreshold, + nmsThreshold, indices); + std::vector finalDetections; for (int idx : indices) { finalDetections.push_back(detections[idx]); } - + return finalDetections; } default: @@ -286,12 +304,9 @@ std::vector ComputerVision::detectObjects(const blob& input, #endif } -std::vector ComputerVision::detectFaces(const blob& input, - FaceModel model, - int minFaceSize, - double scaleFactor, - bool detectLandmarks, - bool recognizeFaces) const { +std::vector ComputerVision::detectFaces( + const blob& input, FaceModel model, int minFaceSize, double scaleFactor, + bool detectLandmarks, bool recognizeFaces) const { if (input.isEmpty()) { return {}; } @@ -310,12 +325,14 @@ std::vector ComputerVision::detectFaces(const blob& input, switch (model) { case FaceModel::HAAR_CASCADE: { cv::CascadeClassifier classifier; - std::string cascadePath = "haarcascade_frontalface_alt.xml"; // Assume in data dir + std::string cascadePath = + "haarcascade_frontalface_alt.xml"; // Assume in data dir if (!classifier.load(cascadePath)) { THROW_RUNTIME_ERROR("Could not load Haar cascade"); } std::vector rects; - classifier.detectMultiScale(gray, rects, scaleFactor, 3, 0, cv::Size(minFaceSize, minFaceSize)); + classifier.detectMultiScale(gray, rects, scaleFactor, 3, 0, + cv::Size(minFaceSize, minFaceSize)); for (const auto& rect : rects) { FaceDetection fd; fd.x = static_cast(rect.x); @@ -324,11 +341,14 @@ std::vector ComputerVision::detectFaces(const blob& input, fd.height = static_cast(rect.height); fd.confidence = 1.0; if (detectLandmarks) { - // Basic landmark detection using another cascade or simple points - // Placeholder: add 5 points for eyes, nose, mouth - fd.landmarks = {{fd.x + fd.width*0.3, fd.y + fd.height*0.3}, {fd.x + fd.width*0.7, fd.y + fd.height*0.3}, - {fd.x + fd.width*0.5, fd.y + fd.height*0.5}, {fd.x + fd.width*0.3, fd.y + fd.height*0.7}, - {fd.x + fd.width*0.7, fd.y + fd.height*0.7}}; + // Basic landmark detection using another cascade or simple + // points Placeholder: add 5 points for eyes, nose, mouth + fd.landmarks = { + {fd.x + fd.width * 0.3, fd.y + fd.height * 0.3}, + {fd.x + fd.width * 0.7, fd.y + fd.height * 0.3}, + {fd.x + fd.width * 0.5, fd.y + fd.height * 0.5}, + {fd.x + fd.width * 0.3, fd.y + fd.height * 0.7}, + {fd.x + fd.width * 0.7, fd.y + fd.height * 0.7}}; } if (recognizeFaces) { // Extract embedding using simple averaging or dnn @@ -342,18 +362,24 @@ std::vector ComputerVision::detectFaces(const blob& input, case FaceModel::DNN_FACE: { std::string modelPath = "opencv_face_detector_uint8.pb"; std::string configPath = "opencv_face_detector.pbtxt"; - cv::dnn::Net net = cv::dnn::readNetFromTensorflow(modelPath, configPath); + cv::dnn::Net net = + cv::dnn::readNetFromTensorflow(modelPath, configPath); cv::Mat blob; - cv::dnn::blobFromImage(src, blob, 1.0, cv::Size(300, 300), cv::Scalar(104, 117, 123), false, false); + cv::dnn::blobFromImage(src, blob, 1.0, cv::Size(300, 300), + cv::Scalar(104, 117, 123), false, false); net.setInput(blob); cv::Mat detection = net.forward(); for (int i = 0; i < detection.rows; ++i) { double conf = detection.at(i, 2); if (conf > 0.5) { - int x1 = static_cast(detection.at(i, 3) * src.cols); - int y1 = static_cast(detection.at(i, 4) * src.rows); - int x2 = static_cast(detection.at(i, 5) * src.cols); - int y2 = static_cast(detection.at(i, 6) * src.rows); + int x1 = + static_cast(detection.at(i, 3) * src.cols); + int y1 = + static_cast(detection.at(i, 4) * src.rows); + int x2 = + static_cast(detection.at(i, 5) * src.cols); + int y2 = + static_cast(detection.at(i, 6) * src.rows); FaceDetection fd; fd.x = static_cast(x1); fd.y = static_cast(y1); @@ -373,13 +399,15 @@ std::vector ComputerVision::detectFaces(const blob& input, } break; } - // Add cases for other FaceModel types similarly, using appropriate OpenCV functions or dnn nets + // Add cases for other FaceModel types similarly, using appropriate + // OpenCV functions or dnn nets default: THROW_RUNTIME_ERROR("Unsupported face model"); } // For other models like MTCNN, RetinaFace, use dnn with respective models - // Age, gender, emotion can be added using additional dnn models if detectLandmarks or recognizeFaces + // Age, gender, emotion can be added using additional dnn models if + // detectLandmarks or recognizeFaces return faces; #else @@ -387,10 +415,9 @@ std::vector ComputerVision::detectFaces(const blob& input, #endif } -std::vector> ComputerVision::segmentImage(const blob& input, - SegmentationMethod method, - int numSegments, - double compactness) const { +std::vector> ComputerVision::segmentImage( + const blob& input, SegmentationMethod method, int numSegments, + double compactness) const { if (input.isEmpty()) { return {}; } @@ -405,14 +432,16 @@ std::vector> ComputerVision::segmentImage(const blob& input, } cv::Mat labels; - std::vector> segmentation(src.rows, std::vector(src.cols, 0)); + std::vector> segmentation(src.rows, + std::vector(src.cols, 0)); switch (method) { case SegmentationMethod::WATERSHED: { cv::Mat markers = cv::Mat::zeros(gray.size(), CV_32S); // Simple marker creation: threshold and distance transform cv::Mat thresh; - cv::threshold(gray, thresh, 0, 255, cv::THRESH_BINARY_INV + cv::THRESH_OTSU); + cv::threshold(gray, thresh, 0, 255, + cv::THRESH_BINARY_INV + cv::THRESH_OTSU); cv::Mat dist; cv::distanceTransform(thresh, dist, cv::Mat(), cv::DIST_L2, 3); cv::normalize(dist, dist, 0, 1.0, cv::NORM_MINMAX); @@ -429,11 +458,14 @@ std::vector> ComputerVision::segmentImage(const blob& input, } case SegmentationMethod::GRABCUT: { cv::Mat bgdModel, fgdModel; - cv::Rect rect(50, 50, src.cols-100, src.rows-100); // Initial rect - cv::grabCut(src, labels, rect, bgdModel, fgdModel, 5, cv::GC_INIT_WITH_RECT); + cv::Rect rect(50, 50, src.cols - 100, + src.rows - 100); // Initial rect + cv::grabCut(src, labels, rect, bgdModel, fgdModel, 5, + cv::GC_INIT_WITH_RECT); for (int y = 0; y < src.rows; ++y) { for (int x = 0; x < src.cols; ++x) { - segmentation[y][x] = labels.at(y, x) / 64; // 0,1,2,3 to 0,1 + segmentation[y][x] = + labels.at(y, x) / 64; // 0,1,2,3 to 0,1 } } break; @@ -460,8 +492,11 @@ std::vector> ComputerVision::segmentImage(const blob& input, gray.convertTo(data, CV_32F); data = data.reshape(1, gray.rows * gray.cols); cv::Mat samples(data.size(), CV_32F); - cv::kmeans(data, numSegments, labels, cv::TermCriteria(cv::TermCriteria::EPS + cv::TermCriteria::COUNT, 10, 1.0), - 3, cv::KMEANS_PP_CENTERS, samples); + cv::kmeans( + data, numSegments, labels, + cv::TermCriteria( + cv::TermCriteria::EPS + cv::TermCriteria::COUNT, 10, 1.0), + 3, cv::KMEANS_PP_CENTERS, samples); labels = labels.reshape(1, gray.rows); for (int y = 0; y < src.rows; ++y) { for (int x = 0; x < src.cols; ++x) { @@ -470,13 +505,15 @@ std::vector> ComputerVision::segmentImage(const blob& input, } break; } - // For SEMANTIC_SEG, INSTANCE_SEG, use dnn segmentation models like DeepLab + // For SEMANTIC_SEG, INSTANCE_SEG, use dnn segmentation models like + // DeepLab case SegmentationMethod::SEMANTIC_SEG: { // Placeholder dnn implementation std::string modelPath = "deeplabv3.pb"; cv::dnn::Net net = cv::dnn::readNet(modelPath); cv::Mat blob; - cv::dnn::blobFromImage(src, blob, 1.0/255.0, cv::Size(513, 513), cv::Scalar(), true, false); + cv::dnn::blobFromImage(src, blob, 1.0 / 255.0, cv::Size(513, 513), + cv::Scalar(), true, false); net.setInput(blob); cv::Mat output = net.forward(); // Process output to labels @@ -505,15 +542,16 @@ std::vector> ComputerVision::trackObjects( const std::vector& frames, const std::vector& initialDetections, const std::string& tracker) const { - // Tracking functionality temporarily disabled due to OpenCV API compatibility issues - (void)frames; (void)initialDetections; (void)tracker; // suppress unused warnings + // Tracking functionality temporarily disabled due to OpenCV API + // compatibility issues + (void)frames; + (void)initialDetections; + (void)tracker; // suppress unused warnings return std::vector>(); } std::vector> ComputerVision::estimateOpticalFlow( - const blob& frame1, - const blob& frame2, - const std::string& method, + const blob& frame1, const blob& frame2, const std::string& method, const std::vector& features) const { if (frame1.isEmpty() || frame2.isEmpty()) { return {}; @@ -523,10 +561,14 @@ std::vector> ComputerVision::estimateOpticalFlow( cv::Mat prev = frame1.to_mat(); cv::Mat next = frame2.to_mat(); cv::Mat prevGray, nextGray; - if (prev.channels() > 1) cv::cvtColor(prev, prevGray, cv::COLOR_BGR2GRAY); - else prevGray = prev; - if (next.channels() > 1) cv::cvtColor(next, nextGray, cv::COLOR_BGR2GRAY); - else nextGray = next; + if (prev.channels() > 1) + cv::cvtColor(prev, prevGray, cv::COLOR_BGR2GRAY); + else + prevGray = prev; + if (next.channels() > 1) + cv::cvtColor(next, nextGray, cv::COLOR_BGR2GRAY); + else + nextGray = next; std::vector prevPts; if (features.empty()) { @@ -534,7 +576,8 @@ std::vector> ComputerVision::estimateOpticalFlow( cv::goodFeaturesToTrack(prevGray, prevPts, 100, 0.01, 10); } else { for (const auto& kp : features) { - prevPts.emplace_back(static_cast(kp.x), static_cast(kp.y)); + prevPts.emplace_back(static_cast(kp.x), + static_cast(kp.y)); } } @@ -543,7 +586,8 @@ std::vector> ComputerVision::estimateOpticalFlow( std::vector nextPts; std::vector status; std::vector err; - cv::calcOpticalFlowPyrLK(prevGray, nextGray, prevPts, nextPts, status, err); + cv::calcOpticalFlowPyrLK(prevGray, nextGray, prevPts, nextPts, status, + err); for (size_t i = 0; i < nextPts.size(); ++i) { if (status[i]) { double dx = nextPts[i].x - prevPts[i].x; @@ -553,18 +597,21 @@ std::vector> ComputerVision::estimateOpticalFlow( } } else if (method == "farneback") { cv::Mat flow; - cv::calcOpticalFlowFarneback(prevGray, nextGray, flow, 0.5, 3, 15, 3, 5, 1.2, 0); + cv::calcOpticalFlowFarneback(prevGray, nextGray, flow, 0.5, 3, 15, 3, 5, + 1.2, 0); // Sample at prevPts locations for (const auto& pt : prevPts) { cv::Point2f p(static_cast(pt.x), static_cast(pt.y)); if (p.x > 0 && p.y > 0 && p.x < flow.cols && p.y < flow.rows) { const cv::Point2f& fxy = flow.at(p.y, p.x); - flows.emplace_back(static_cast(fxy.x), static_cast(fxy.y)); + flows.emplace_back(static_cast(fxy.x), + static_cast(fxy.y)); } } } else if (method == "tvl1") { // Optical flow functionality temporarily disabled - THROW_RUNTIME_ERROR("TVL1 optical flow not available in this OpenCV version"); + THROW_RUNTIME_ERROR( + "TVL1 optical flow not available in this OpenCV version"); } return flows; @@ -574,10 +621,8 @@ std::vector> ComputerVision::estimateOpticalFlow( } std::vector> ComputerVision::classifyImage( - const blob& input, - const std::string& modelType, - const std::string& modelPath, - int topK) const { + const blob& input, const std::string& modelType, + const std::string& modelPath, int topK) const { if (input.isEmpty()) { return {}; } @@ -592,20 +637,26 @@ std::vector> ComputerVision::classifyImage( } net = cv::dnn::readNetFromTorch(modelPath); } else if (modelType == "mobilenet") { - net = cv::dnn::readNetFromTensorflow(modelPath + "/mobilenet_v1_1.0_224.pb", modelPath + "/mobilenet_v1_1.0_224.pbtxt"); - } // Add other models + net = cv::dnn::readNetFromTensorflow( + modelPath + "/mobilenet_v1_1.0_224.pb", + modelPath + "/mobilenet_v1_1.0_224.pbtxt"); + } // Add other models cv::Mat blob; - cv::dnn::blobFromImage(src, blob, 1.0/255.0, cv::Size(224, 224), cv::Scalar(0.485, 0.456, 0.406), true, false); + cv::dnn::blobFromImage(src, blob, 1.0 / 255.0, cv::Size(224, 224), + cv::Scalar(0.485, 0.456, 0.406), true, false); net.setInput(blob); cv::Mat prob = net.forward(); std::vector> results; - std::vector confidences(prob.ptr(), prob.ptr() + prob.total()); + std::vector confidences(prob.ptr(), + prob.ptr() + prob.total()); std::vector indices(confidences.size()); std::iota(indices.begin(), indices.end(), 0); std::partial_sort(indices.begin(), indices.begin() + topK, indices.end(), - [&confidences](size_t i, size_t j) { return confidences[i] > confidences[j]; }); + [&confidences](size_t i, size_t j) { + return confidences[i] > confidences[j]; + }); for (int i = 0; i < std::min(topK, static_cast(indices.size())); ++i) { size_t idx = indices[i]; @@ -618,11 +669,10 @@ std::vector> ComputerVision::classifyImage( #endif } -std::vector> ComputerVision::recognizeText( - const blob& input, - const std::string& language, - const std::string& ocrEngine, - bool preprocessImage) const { +std::vector> +ComputerVision::recognizeText(const blob& input, const std::string& language, + const std::string& ocrEngine, + bool preprocessImage) const { if (input.isEmpty()) { return {}; } @@ -635,7 +685,8 @@ std::vector> Com src = processed; } - std::vector> texts; + std::vector> + texts; // Basic text region detection using MSER or contours std::vector> contours; @@ -647,23 +698,25 @@ std::vector> Com if (contourArea(cnt) > 100) { // Filter small regions Rect bbox = boundingRect(cnt); // Dummy text and confidence since no OCR - std::string dummyText = "detected_text_" + std::to_string(contours.size()); + std::string dummyText = + "detected_text_" + std::to_string(contours.size()); double conf = 0.7; - texts.emplace_back(dummyText, conf, static_cast(bbox.x), static_cast(bbox.y), - static_cast(bbox.width), static_cast(bbox.height)); + texts.emplace_back(dummyText, conf, static_cast(bbox.x), + static_cast(bbox.y), + static_cast(bbox.width), + static_cast(bbox.height)); } } // For real OCR, recommend linking Tesseract or using external lib - // If ocrEngine == "paddleocr" etc., could call external, but not in pure OpenCV + // If ocrEngine == "paddleocr" etc., could call external, but not in pure + // OpenCV return texts; } std::vector> ComputerVision::estimatePose( - const blob& input, - const std::string& model, - bool detectHands, + const blob& input, const std::string& model, bool detectHands, bool detectFace) const { if (input.isEmpty()) { return {{}}; @@ -682,7 +735,7 @@ std::vector> ComputerVision::estimatePose( keypoints.emplace_back(100, 200, 1, 0, 1, 0, 0); // Nose // Add more dummy keypoints for body parts for (int i = 1; i < 15; ++i) { - keypoints.emplace_back(100 + i*20, 200 + i*10, 1, 0, 1, 0, i); + keypoints.emplace_back(100 + i * 20, 200 + i * 10, 1, 0, 1, 0, i); } } else if (model == "mediapipe") { // External, placeholder similar @@ -712,8 +765,7 @@ std::vector> ComputerVision::estimatePose( } std::unordered_map ComputerVision::analyzeQuality( - const blob& input, - const std::vector& metrics) const { + const blob& input, const std::vector& metrics) const { if (input.isEmpty()) { return {}; } @@ -734,11 +786,12 @@ std::unordered_map ComputerVision::analyzeQuality( cv::Mat laplacian, mean, stddev; cv::Laplacian(gray, laplacian, CV_64F); cv::meanStdDev(laplacian, mean, stddev); - results["sharpness"] = stddev.at(0, 0) * stddev.at(0, 0); + results["sharpness"] = + stddev.at(0, 0) * stddev.at(0, 0); } else if (metric == "noise") { // Simple noise estimation using wavelet or block variance cv::Mat blurred; - cv::GaussianBlur(gray, blurred, cv::Size(3,3), 0); + cv::GaussianBlur(gray, blurred, cv::Size(3, 3), 0); cv::Mat diff = gray - blurred; cv::Scalar s = cv::sum(diff); double noise = sqrt(s[0] * s[0] / (diff.rows * diff.cols)); @@ -759,11 +812,11 @@ std::unordered_map ComputerVision::analyzeQuality( #endif } -std::pair>> ComputerVision::detectAnomalies( - const blob& input, - const std::vector& referenceImages, - const std::string& method, - double threshold) const { +std::pair>> +ComputerVision::detectAnomalies(const blob& input, + const std::vector& referenceImages, + const std::string& method, + double threshold) const { if (input.isEmpty() || referenceImages.empty()) { return {0.0, {}}; } @@ -793,8 +846,10 @@ std::pair>> ComputerVision::detectAnomalies for (const auto& r : refs) { cv::Mat diff = r - refMean; diff = diff.mul(diff); - if (diffSum.empty()) diffSum = diff; - else diffSum += diff; + if (diffSum.empty()) + diffSum = diff; + else + diffSum += diff; } cv::sqrt(diffSum / static_cast(refs.size()), refStd); } @@ -810,13 +865,15 @@ std::pair>> ComputerVision::detectAnomalies cv::Mat thresh; cv::threshold(anomalyMap, thresh, threshold, 255, cv::THRESH_BINARY); std::vector> contours; - cv::findContours(thresh, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + cv::findContours(thresh, contours, cv::RETR_EXTERNAL, + cv::CHAIN_APPROX_SIMPLE); for (const auto& cnt : contours) { std::vector region; cv::Rect bb = cv::boundingRect(cnt); for (int y = bb.y; y < bb.y + bb.height; ++y) { for (int x = bb.x; x < bb.x + bb.width; ++x) { - region.push_back(y * img.cols + x); // Linear indices or 2D? + region.push_back(y * img.cols + + x); // Linear indices or 2D? } } regions.push_back(region); // Adjust to 2D if needed @@ -834,9 +891,9 @@ std::pair>> ComputerVision::detectAnomalies #endif } -std::vector ComputerVision::extractFeatures(const blob& input, - const std::string& model, - const std::string& layer) const { +std::vector ComputerVision::extractFeatures( + const blob& input, const std::string& model, + const std::string& layer) const { if (input.isEmpty()) { return {}; } @@ -848,7 +905,7 @@ std::vector ComputerVision::extractFeatures(const blob& input, modelPath = "resnet18.onnx"; // Assume ONNX } else if (model == "vgg") { modelPath = "vgg16.onnx"; - } // etc. + } // etc. if (modelPath.empty()) { THROW_RUNTIME_ERROR("Model path required for feature extraction"); @@ -856,14 +913,18 @@ std::vector ComputerVision::extractFeatures(const blob& input, cv::dnn::Net net = cv::dnn::readNet(modelPath); cv::Mat blob; - cv::dnn::blobFromImage(src, blob, 1.0/255.0, cv::Size(224, 224), cv::Scalar(0.485, 0.456, 0.406), true, false); + cv::dnn::blobFromImage(src, blob, 1.0 / 255.0, cv::Size(224, 224), + cv::Scalar(0.485, 0.456, 0.406), true, false); net.setInput(blob); std::vector outputs; - std::vector outNames = layer.empty() ? net.getUnconnectedOutLayersNames() : std::vector{layer}; + std::vector outNames = layer.empty() + ? net.getUnconnectedOutLayersNames() + : std::vector{layer}; net.forward(outputs, outNames); - std::vector features(outputs[0].ptr(), outputs[0].ptr() + outputs[0].total()); + std::vector features(outputs[0].ptr(), + outputs[0].ptr() + outputs[0].total()); return features; #else @@ -872,36 +933,46 @@ std::vector ComputerVision::extractFeatures(const blob& input, } std::vector> ComputerVision::findSimilarImages( - const blob& query, - const std::vector& database, - int topK, + const blob& query, const std::vector& database, int topK, const std::string& metric) const { if (query.isEmpty() || database.empty()) { return {}; } - std::vector queryFeat = extractFeatures(query, "resnet"); // Use default model + std::vector queryFeat = + extractFeatures(query, "resnet"); // Use default model std::vector> similarities; for (size_t i = 0; i < database.size(); ++i) { std::vector dbFeat = extractFeatures(database[i], "resnet"); double dist; if (metric == "cosine") { - dist = 1.0 - std::inner_product(queryFeat.begin(), queryFeat.end(), dbFeat.begin(), 0.0) / - (std::sqrt(std::inner_product(queryFeat.begin(), queryFeat.end(), queryFeat.begin(), 0.0)) * - std::sqrt(std::inner_product(dbFeat.begin(), dbFeat.end(), dbFeat.begin(), 0.0))); + dist = + 1.0 - + std::inner_product(queryFeat.begin(), queryFeat.end(), + dbFeat.begin(), 0.0) / + (std::sqrt(std::inner_product(queryFeat.begin(), + queryFeat.end(), + queryFeat.begin(), 0.0)) * + std::sqrt(std::inner_product(dbFeat.begin(), dbFeat.end(), + dbFeat.begin(), 0.0))); } else if (metric == "euclidean") { - dist = std::sqrt(std::inner_product(queryFeat.begin(), queryFeat.end(), dbFeat.begin(), 0.0, - std::plus<>(), [](float a, float b){ return (a-b)*(a-b); })); + dist = std::sqrt(std::inner_product( + queryFeat.begin(), queryFeat.end(), dbFeat.begin(), 0.0, + std::plus<>(), + [](float a, float b) { return (a - b) * (a - b); })); } else { // manhattan - dist = std::inner_product(queryFeat.begin(), queryFeat.end(), dbFeat.begin(), 0.0, - std::plus<>(), [](float a, float b){ return std::abs(a-b); }); + dist = std::inner_product( + queryFeat.begin(), queryFeat.end(), dbFeat.begin(), 0.0, + std::plus<>(), + [](float a, float b) { return std::abs(a - b); }); } similarities.emplace_back(static_cast(i), dist); } - std::partial_sort(similarities.begin(), similarities.begin() + topK, similarities.end(), - [](const auto& a, const auto& b){ return a.second < b.second; }); + std::partial_sort( + similarities.begin(), similarities.begin() + topK, similarities.end(), + [](const auto& a, const auto& b) { return a.second < b.second; }); similarities.resize(topK); @@ -909,13 +980,16 @@ std::vector> ComputerVision::findSimilarImages( } // Update existing detectObjects to use double for bbox -// In the processing, cast to double: detection.x = static_cast((data[0] - data[2]/2) * src.cols); +// In the processing, cast to double: detection.x = static_cast((data[0] +// - data[2]/2) * src.cols); -// Similar for other existing functions like trackObject - remove it, as trackObjects is the one +// Similar for other existing functions like trackObject - remove it, as +// trackObjects is the one // For preprocessImage, keep existing but fix to return vector correctly -bool ComputerVision::initializeModel(const std::string& modelType, const std::string& modelPath) const { +bool ComputerVision::initializeModel(const std::string& modelType, + const std::string& modelPath) const { // For const correctness, perhaps load on demand in methods // Return true if path valid or built-in return !modelPath.empty() || modelType == "builtin"; @@ -924,11 +998,12 @@ bool ComputerVision::initializeModel(const std::string& modelType, const std::st // Remove wrong signatures like std::vector, blob for segment, etc. // Add the factory at the end -std::unique_ptr createOptimalComputerVision(bool useGPU, const std::string& modelPath) { +std::unique_ptr createOptimalComputerVision( + bool useGPU, const std::string& modelPath) { auto cvision = std::make_unique(); // Note: initializeModel is protected, cannot be called from here // User should call it separately if needed - (void)modelPath; // suppress unused parameter warning + (void)modelPath; // suppress unused parameter warning return cvision; } diff --git a/atom/image/processing/computer_vision.hpp b/atom/image/processing/computer_vision.hpp index 9590956b..d968d6df 100644 --- a/atom/image/processing/computer_vision.hpp +++ b/atom/image/processing/computer_vision.hpp @@ -14,12 +14,12 @@ * @version 1.0.0 */ -#include "../core/image_blob.hpp" -#include #include #include #include #include +#include +#include "../core/image_blob.hpp" namespace atom::image { @@ -27,59 +27,59 @@ namespace atom::image { * @brief Feature detector types */ enum class FeatureDetectorType { - SIFT, // Scale-Invariant Feature Transform - SURF, // Speeded-Up Robust Features - ORB, // Oriented FAST and Rotated BRIEF - AKAZE, // Accelerated-KAZE - BRISK, // Binary Robust Invariant Scalable Keypoints - FAST, // Features from Accelerated Segment Test - HARRIS, // Harris corner detector - GFTT, // Good Features to Track - MSER, // Maximally Stable Extremal Regions - BLOB // Blob detector + SIFT, // Scale-Invariant Feature Transform + SURF, // Speeded-Up Robust Features + ORB, // Oriented FAST and Rotated BRIEF + AKAZE, // Accelerated-KAZE + BRISK, // Binary Robust Invariant Scalable Keypoints + FAST, // Features from Accelerated Segment Test + HARRIS, // Harris corner detector + GFTT, // Good Features to Track + MSER, // Maximally Stable Extremal Regions + BLOB // Blob detector }; /** * @brief Object detection models */ enum class ObjectDetectionModel { - YOLO_V5, // YOLOv5 object detection - YOLO_V8, // YOLOv8 object detection - SSD, // Single Shot MultiBox Detector - FASTER_RCNN, // Faster R-CNN - MOBILENET, // MobileNet-SSD - EFFICIENTDET, // EfficientDet - DETECTRON2, // Detectron2 models - CUSTOM // Custom trained model + YOLO_V5, // YOLOv5 object detection + YOLO_V8, // YOLOv8 object detection + SSD, // Single Shot MultiBox Detector + FASTER_RCNN, // Faster R-CNN + MOBILENET, // MobileNet-SSD + EFFICIENTDET, // EfficientDet + DETECTRON2, // Detectron2 models + CUSTOM // Custom trained model }; /** * @brief Face detection/recognition models */ enum class FaceModel { - HAAR_CASCADE, // Haar cascade classifier - DNN_FACE, // DNN-based face detection - MTCNN, // Multi-task CNN - RETINAFACE, // RetinaFace - FACENET, // FaceNet for recognition - ARCFACE, // ArcFace for recognition - DLIB_68, // Dlib 68-point landmark detector - MEDIAPIPE // MediaPipe face detection + HAAR_CASCADE, // Haar cascade classifier + DNN_FACE, // DNN-based face detection + MTCNN, // Multi-task CNN + RETINAFACE, // RetinaFace + FACENET, // FaceNet for recognition + ARCFACE, // ArcFace for recognition + DLIB_68, // Dlib 68-point landmark detector + MEDIAPIPE // MediaPipe face detection }; /** * @brief Image segmentation methods */ enum class SegmentationMethod { - WATERSHED, // Watershed segmentation - GRABCUT, // GrabCut algorithm - MEAN_SHIFT, // Mean shift segmentation - FELZENSZWALB, // Felzenszwalb's algorithm - SLIC, // Simple Linear Iterative Clustering - QUICKSHIFT, // Quick shift segmentation - SEMANTIC_SEG, // Semantic segmentation (DNN) - INSTANCE_SEG, // Instance segmentation - PANOPTIC_SEG // Panoptic segmentation + WATERSHED, // Watershed segmentation + GRABCUT, // GrabCut algorithm + MEAN_SHIFT, // Mean shift segmentation + FELZENSZWALB, // Felzenszwalb's algorithm + SLIC, // Simple Linear Iterative Clustering + QUICKSHIFT, // Quick shift segmentation + SEMANTIC_SEG, // Semantic segmentation (DNN) + INSTANCE_SEG, // Instance segmentation + PANOPTIC_SEG // Panoptic segmentation }; /** @@ -96,39 +96,51 @@ struct Keypoint { Keypoint(double x = 0, double y = 0, double size = 1, double angle = -1, double response = 0, int octave = 0, int classId = -1) - : x(x), y(y), size(size), angle(angle), response(response), - octave(octave), classId(classId) {} + : x(x), + y(y), + size(size), + angle(angle), + response(response), + octave(octave), + classId(classId) {} }; /** * @brief Detection result structure */ struct Detection { - int classId; // Object class ID - std::string className; // Object class name - double confidence; // Detection confidence - double x, y, width, height; // Bounding box - std::vector mask; // Segmentation mask (if available) - std::vector keypoints; // Object keypoints (if available) + int classId; // Object class ID + std::string className; // Object class name + double confidence; // Detection confidence + double x, y, width, height; // Bounding box + std::vector mask; // Segmentation mask (if available) + std::vector keypoints; // Object keypoints (if available) Detection(int id = -1, const std::string& name = "", double conf = 0.0, double x = 0, double y = 0, double w = 0, double h = 0) - : classId(id), className(name), confidence(conf), x(x), y(y), width(w), height(h) {} + : classId(id), + className(name), + confidence(conf), + x(x), + y(y), + width(w), + height(h) {} }; /** * @brief Face detection result */ struct FaceDetection { - double x, y, width, height; // Face bounding box - double confidence; // Detection confidence - std::vector landmarks; // Facial landmarks - std::vector embedding; // Face embedding for recognition - double age; // Estimated age - std::string gender; // Estimated gender - std::string emotion; // Dominant emotion - - FaceDetection(double x = 0, double y = 0, double w = 0, double h = 0, double conf = 0.0) + double x, y, width, height; // Face bounding box + double confidence; // Detection confidence + std::vector landmarks; // Facial landmarks + std::vector embedding; // Face embedding for recognition + double age; // Estimated age + std::string gender; // Estimated gender + std::string emotion; // Dominant emotion + + FaceDetection(double x = 0, double y = 0, double w = 0, double h = 0, + double conf = 0.0) : x(x), y(y), width(w), height(h), confidence(conf), age(-1) {} }; @@ -148,16 +160,17 @@ class ComputerVision { * @param qualityLevel Quality threshold for feature detection * @return Vector of detected keypoints */ - virtual std::vector detectFeatures(const blob& input, - FeatureDetectorType detectorType = FeatureDetectorType::ORB, - int maxFeatures = 1000, - double qualityLevel = 0.01) const; + virtual std::vector detectFeatures( + const blob& input, + FeatureDetectorType detectorType = FeatureDetectorType::ORB, + int maxFeatures = 1000, double qualityLevel = 0.01) const; /** * @brief Match features between two images * @param keypoints1 Keypoints from first image * @param keypoints2 Keypoints from second image - * @param matchingMethod Matching method ("brute_force", "flann", "ratio_test") + * @param matchingMethod Matching method ("brute_force", "flann", + * "ratio_test") * @param distanceThreshold Distance threshold for matches * @return Vector of matched keypoint pairs (indices) */ @@ -176,11 +189,11 @@ class ComputerVision { * @param modelPath Path to model file (if custom) * @return Vector of detected objects */ - virtual std::vector detectObjects(const blob& input, - ObjectDetectionModel model = ObjectDetectionModel::YOLO_V5, - double confidenceThreshold = 0.5, - double nmsThreshold = 0.4, - const std::string& modelPath = "") const; + virtual std::vector detectObjects( + const blob& input, + ObjectDetectionModel model = ObjectDetectionModel::YOLO_V5, + double confidenceThreshold = 0.5, double nmsThreshold = 0.4, + const std::string& modelPath = "") const; /** * @brief Detect faces in image @@ -192,12 +205,10 @@ class ComputerVision { * @param recognizeFaces Whether to generate face embeddings * @return Vector of detected faces */ - virtual std::vector detectFaces(const blob& input, - FaceModel model = FaceModel::DNN_FACE, - int minFaceSize = 30, - double scaleFactor = 1.1, - bool detectLandmarks = true, - bool recognizeFaces = false) const; + virtual std::vector detectFaces( + const blob& input, FaceModel model = FaceModel::DNN_FACE, + int minFaceSize = 30, double scaleFactor = 1.1, + bool detectLandmarks = true, bool recognizeFaces = false) const; /** * @brief Segment image into regions @@ -207,10 +218,9 @@ class ComputerVision { * @param compactness Compactness parameter (for SLIC) * @return Segmentation mask (label for each pixel) */ - virtual std::vector> segmentImage(const blob& input, - SegmentationMethod method = SegmentationMethod::SLIC, - int numSegments = 100, - double compactness = 10.0) const; + virtual std::vector> segmentImage( + const blob& input, SegmentationMethod method = SegmentationMethod::SLIC, + int numSegments = 100, double compactness = 10.0) const; /** * @brief Track objects across video frames @@ -228,29 +238,28 @@ class ComputerVision { * @brief Estimate optical flow between two frames * @param frame1 First frame * @param frame2 Second frame - * @param method Flow estimation method ("lucas_kanade", "farneback", "tvl1") + * @param method Flow estimation method ("lucas_kanade", "farneback", + * "tvl1") * @param features Feature points to track (if empty, detect automatically) * @return Flow vectors for each point */ virtual std::vector> estimateOpticalFlow( - const blob& frame1, - const blob& frame2, + const blob& frame1, const blob& frame2, const std::string& method = "lucas_kanade", const std::vector& features = {}) const; /** * @brief Perform image classification * @param input Input image blob - * @param model Classification model ("resnet", "mobilenet", "efficientnet", "custom") + * @param model Classification model ("resnet", "mobilenet", "efficientnet", + * "custom") * @param modelPath Path to model file (if custom) * @param topK Number of top predictions to return * @return Classification results (class, confidence pairs) */ virtual std::vector> classifyImage( - const blob& input, - const std::string& model = "resnet", - const std::string& modelPath = "", - int topK = 5) const; + const blob& input, const std::string& model = "resnet", + const std::string& modelPath = "", int topK = 5) const; /** * @brief Detect and recognize text in image (OCR) @@ -260,9 +269,9 @@ class ComputerVision { * @param preprocessImage Whether to preprocess image for better OCR * @return Detected text with bounding boxes and confidence */ - virtual std::vector> - recognizeText(const blob& input, - const std::string& language = "eng", + virtual std::vector< + std::tuple> + recognizeText(const blob& input, const std::string& language = "eng", const std::string& ocrEngine = "tesseract", bool preprocessImage = true) const; @@ -275,10 +284,8 @@ class ComputerVision { * @return Detected poses with keypoints */ virtual std::vector> estimatePose( - const blob& input, - const std::string& model = "mediapipe", - bool detectHands = false, - bool detectFace = false) const; + const blob& input, const std::string& model = "mediapipe", + bool detectHands = false, bool detectFace = false) const; /** * @brief Analyze image quality @@ -288,32 +295,34 @@ class ComputerVision { */ virtual std::unordered_map analyzeQuality( const blob& input, - const std::vector& metrics = {"sharpness", "noise", "exposure", "contrast"}) const; + const std::vector& metrics = { + "sharpness", "noise", "exposure", "contrast"}) const; /** * @brief Detect image anomalies * @param input Input image blob * @param referenceImages Reference images for comparison - * @param method Anomaly detection method ("statistical", "autoencoder", "one_class_svm") + * @param method Anomaly detection method ("statistical", "autoencoder", + * "one_class_svm") * @param threshold Anomaly threshold * @return Anomaly score and detected regions */ virtual std::pair>> detectAnomalies( - const blob& input, - const std::vector& referenceImages, + const blob& input, const std::vector& referenceImages, const std::string& method = "statistical", double threshold = 0.5) const; /** * @brief Generate image embeddings/features * @param input Input image blob - * @param model Feature extraction model ("resnet", "vgg", "inception", "clip") + * @param model Feature extraction model ("resnet", "vgg", "inception", + * "clip") * @param layer Layer to extract features from * @return Feature vector */ - virtual std::vector extractFeatures(const blob& input, - const std::string& model = "resnet", - const std::string& layer = "pool5") const; + virtual std::vector extractFeatures( + const blob& input, const std::string& model = "resnet", + const std::string& layer = "pool5") const; /** * @brief Find similar images using feature matching @@ -324,9 +333,7 @@ class ComputerVision { * @return Similar images with similarity scores */ virtual std::vector> findSimilarImages( - const blob& query, - const std::vector& database, - int topK = 10, + const blob& query, const std::vector& database, int topK = 10, const std::string& metric = "cosine") const; protected: @@ -336,7 +343,8 @@ class ComputerVision { * @param modelPath Path to model files * @return Success status */ - virtual bool initializeModel(const std::string& modelType, const std::string& modelPath = "") const; + virtual bool initializeModel(const std::string& modelType, + const std::string& modelPath = "") const; /** * @brief Preprocess image for computer vision operations @@ -346,10 +354,10 @@ class ComputerVision { * @param meanSubtraction Mean values for subtraction * @return Preprocessed image data */ - virtual std::vector preprocessImage(const blob& input, - const std::pair& targetSize = {224, 224}, - bool normalize = true, - const std::vector& meanSubtraction = {}) const; + virtual std::vector preprocessImage( + const blob& input, const std::pair& targetSize = {224, 224}, + bool normalize = true, + const std::vector& meanSubtraction = {}) const; }; /** @@ -358,9 +366,9 @@ class ComputerVision { * @param modelPath Path to model files directory * @return Unique pointer to computer vision processor */ -std::unique_ptr createOptimalComputerVision(bool useGPU = false, - const std::string& modelPath = ""); +std::unique_ptr createOptimalComputerVision( + bool useGPU = false, const std::string& modelPath = ""); -} // namespace atom::image +} // namespace atom::image -#endif // ATOM_IMAGE_COMPUTER_VISION_HPP +#endif // ATOM_IMAGE_COMPUTER_VISION_HPP diff --git a/atom/image/processing/enhancement.cpp b/atom/image/processing/enhancement.cpp index 3b76fddb..056f6528 100644 --- a/atom/image/processing/enhancement.cpp +++ b/atom/image/processing/enhancement.cpp @@ -11,8 +11,8 @@ #define THROW_INVALID_ARGUMENT(msg) throw std::invalid_argument(msg) #ifdef ATOM_IMAGE_HAS_OPENCV -#include #include +#include #include #endif @@ -22,9 +22,9 @@ namespace atom::image { -blob ImageEnhancement::equalizeHistogram(const blob& input, - HistogramMethod method, - const EnhancementParams& params) const { +blob ImageEnhancement::equalizeHistogram( + const blob& input, HistogramMethod method, + const EnhancementParams& params) const { if (input.isEmpty()) { return blob{}; } @@ -52,7 +52,9 @@ blob ImageEnhancement::equalizeHistogram(const blob& input, break; } case HistogramMethod::CLAHE: { - auto clahe = cv::createCLAHE(params.clipLimit, cv::Size(params.tileGridSize, params.tileGridSize)); + auto clahe = cv::createCLAHE( + params.clipLimit, + cv::Size(params.tileGridSize, params.tileGridSize)); if (src.channels() == 1) { clahe->apply(src, dst); } else { @@ -94,9 +96,9 @@ blob ImageEnhancement::equalizeHistogram(const blob& input, } blob ImageEnhancement::adjustBrightnessContrast(const blob& input, - double brightness, - double contrast, - bool preserveDetails) const { + double brightness, + double contrast, + bool preserveDetails) const { if (input.isEmpty()) { return blob{}; } @@ -105,31 +107,31 @@ blob ImageEnhancement::adjustBrightnessContrast(const blob& input, #ifdef ATOM_IMAGE_HAS_OPENCV cv::Mat src = input.to_mat(); cv::Mat dst; - + // Convert brightness from [-100, 100] to additive value - double addValue = brightness * 2.55; // Scale to [0, 255] range + double addValue = brightness * 2.55; // Scale to [0, 255] range src.convertTo(dst, -1, contrast, addValue); - + return blob(dst); #else // Manual implementation for brightness adjustment blob result = input.clone(); int brightnessValue = static_cast(brightness * 2.55); - + for (size_t i = 0; i < result.size(); ++i) { - int pixel = std::min(255, std::max(0, static_cast(result[i]) * static_cast(contrast) + brightnessValue)); + int pixel = + std::min(255, std::max(0, static_cast(result[i]) * + static_cast(contrast) + + brightnessValue)); result[i] = static_cast(pixel); } - + return result; #endif } - - -blob ImageEnhancement::gammaCorrection(const blob& input, - double gamma, - ColorSpace colorSpace) const { +blob ImageEnhancement::gammaCorrection(const blob& input, double gamma, + ColorSpace colorSpace) const { if (input.isEmpty()) { return blob{}; } @@ -138,9 +140,10 @@ blob ImageEnhancement::gammaCorrection(const blob& input, // Create gamma correction lookup table std::array lookupTable; double invGamma = 1.0 / gamma; - + for (int i = 0; i < 256; ++i) { - lookupTable[i] = static_cast(std::pow(i / 255.0, invGamma) * 255.0); + lookupTable[i] = + static_cast(std::pow(i / 255.0, invGamma) * 255.0); } #ifdef ATOM_IMAGE_HAS_OPENCV @@ -148,24 +151,23 @@ blob ImageEnhancement::gammaCorrection(const blob& input, cv::Mat dst; cv::Mat lut(1, 256, CV_8U, lookupTable.data()); cv::LUT(src, lut, dst); - + return blob(dst); #else // Manual gamma correction blob result = input.clone(); - + for (size_t i = 0; i < result.size(); ++i) { uint8_t pixel = static_cast(result[i]); result[i] = static_cast(lookupTable[pixel]); } - + return result; #endif } -blob ImageEnhancement::vibranceSaturation(const blob& input, - double vibrance, - double saturation) const { +blob ImageEnhancement::vibranceSaturation(const blob& input, double vibrance, + double saturation) const { if (input.isEmpty()) { return blob{}; } @@ -174,30 +176,30 @@ blob ImageEnhancement::vibranceSaturation(const blob& input, #ifdef ATOM_IMAGE_HAS_OPENCV cv::Mat src = input.to_mat(); cv::Mat dst; - + if (src.channels() >= 3) { cv::Mat hsv; cv::cvtColor(src, hsv, cv::COLOR_BGR2HSV); - + std::vector channels; cv::split(hsv, channels); - + // Adjust saturation channel - double factorS = (saturation + vibrance) / 200.0 + 1.0; // Combined + double factorS = (saturation + vibrance) / 200.0 + 1.0; // Combined channels[1].convertTo(channels[1], -1, factorS, 0); - + cv::merge(channels, hsv); cv::cvtColor(hsv, dst, cv::COLOR_HSV2BGR); } else { - dst = src.clone(); // No saturation adjustment for grayscale + dst = src.clone(); // No saturation adjustment for grayscale } - + return blob(dst); #else // Manual vibrance and saturation adjustment blob result = input.clone(); (void)vibrance; - (void)saturation; // For manual, simple multiply average + (void)saturation; // For manual, simple multiply average double avgFactor = (vibrance + saturation) / 200.0 + 1.0; for (size_t i = 0; i < result.size(); ++i) { int val = static_cast(result[i]) * avgFactor; @@ -207,11 +209,8 @@ blob ImageEnhancement::vibranceSaturation(const blob& input, #endif } - - -blob ImageEnhancement::toneMapping(const blob& input, - ToneMappingOperator op, - const EnhancementParams& params) const { +blob ImageEnhancement::toneMapping(const blob& input, ToneMappingOperator op, + const EnhancementParams& params) const { if (input.isEmpty()) { return blob{}; } @@ -220,46 +219,47 @@ blob ImageEnhancement::toneMapping(const blob& input, #ifdef ATOM_IMAGE_HAS_OPENCV cv::Mat src = input.to_mat(); cv::Mat dst; - + // Convert to float for tone mapping cv::Mat floatSrc; - src.convertTo(floatSrc, CV_32F, 1.0/255.0); - + src.convertTo(floatSrc, CV_32F, 1.0 / 255.0); + switch (op) { case ToneMappingOperator::REINHARD: { - auto tonemap = cv::createTonemapReinhard(params.gamma, params.intensity, - params.lightAdaptation, params.colorAdaptation); + auto tonemap = cv::createTonemapReinhard( + params.gamma, params.intensity, params.lightAdaptation, + params.colorAdaptation); tonemap->process(floatSrc, dst); break; } case ToneMappingOperator::DRAGO: { - auto tonemap = cv::createTonemapDrago(params.gamma, params.saturation); + auto tonemap = + cv::createTonemapDrago(params.gamma, params.saturation); tonemap->process(floatSrc, dst); break; } case ToneMappingOperator::MANTIUK: { - auto tonemap = cv::createTonemapMantiuk(params.gamma, params.saturation); + auto tonemap = + cv::createTonemapMantiuk(params.gamma, params.saturation); tonemap->process(floatSrc, dst); break; } default: THROW_RUNTIME_ERROR("Unsupported tone mapping operator"); } - + // Convert back to 8-bit dst.convertTo(dst, CV_8U, 255.0); - + return blob(dst); #else THROW_RUNTIME_ERROR("OpenCV required for tone mapping"); #endif } - - blob ImageEnhancement::colorCorrection(const blob& input, - ColorCorrectionMethod method, - const EnhancementParams& params) const { + ColorCorrectionMethod method, + const EnhancementParams& params) const { if (input.isEmpty()) { return blob{}; } @@ -273,8 +273,9 @@ blob ImageEnhancement::colorCorrection(const blob& input, // Simple white balance using gray world assumption cv::Scalar mean = cv::mean(src); double avgR = mean[2], avgG = mean[1], avgB = mean[0]; - double scaleR = 128.0 / avgR, scaleG = 128.0 / avgG, scaleB = 128.0 / avgB; - + double scaleR = 128.0 / avgR, scaleG = 128.0 / avgG, + scaleB = 128.0 / avgB; + std::vector channels; cv::split(src, channels); channels[0].convertTo(channels[0], -1, scaleB); @@ -284,7 +285,8 @@ blob ImageEnhancement::colorCorrection(const blob& input, break; } case ColorCorrectionMethod::COLOR_CAST: { - // Color cast removal using histogram matching or simple normalization + // Color cast removal using histogram matching or simple + // normalization dst = src.clone(); // Placeholder: normalize channels std::vector channels; @@ -301,16 +303,24 @@ blob ImageEnhancement::colorCorrection(const blob& input, } case ColorCorrectionMethod::CURVES: { // Apply simple S-curve - dst = applyCurve(input, {{0,0}, {64, 64*0.8}, {128, 128*1.2}, {192, 192*1.1}, {255,255}}).to_mat(); + dst = applyCurve(input, {{0, 0}, + {64, 64 * 0.8}, + {128, 128 * 1.2}, + {192, 192 * 1.1}, + {255, 255}}) + .to_mat(); break; } case ColorCorrectionMethod::LEVELS: { - dst = adjustLevels(input, params.blackPoint[0], params.whitePoint[0], params.gamma).to_mat(); + dst = adjustLevels(input, params.blackPoint[0], + params.whitePoint[0], params.gamma) + .to_mat(); break; } case ColorCorrectionMethod::COLOR_GRADING: { // Advanced grading: adjust shadows, midtones, highlights - dst = shadowHighlight(input, params.shadows, params.highlights).to_mat(); + dst = shadowHighlight(input, params.shadows, params.highlights) + .to_mat(); break; } case ColorCorrectionMethod::AUTO_LEVELS: { @@ -324,13 +334,17 @@ blob ImageEnhancement::colorCorrection(const blob& input, cv::minMaxLoc(hist, &minVal, &maxVal); // Simple stretch dst = src.clone(); - dst.convertTo(dst, -1, 255.0 / (maxVal - minVal), -minVal * 255.0 / (maxVal - minVal)); + dst.convertTo(dst, -1, 255.0 / (maxVal - minVal), + -minVal * 255.0 / (maxVal - minVal)); break; } case ColorCorrectionMethod::AUTO_COLOR: { // Auto color: white balance + levels - auto wb = colorCorrection(input, ColorCorrectionMethod::WHITE_BALANCE, params); - dst = adjustLevels(wb, params.blackPoint[0], params.whitePoint[0], params.gamma).to_mat(); // Use [0] for luminance + auto wb = colorCorrection( + input, ColorCorrectionMethod::WHITE_BALANCE, params); + dst = adjustLevels(wb, params.blackPoint[0], params.whitePoint[0], + params.gamma) + .to_mat(); // Use [0] for luminance break; } default: @@ -346,7 +360,8 @@ blob ImageEnhancement::colorCorrection(const blob& input, result = gammaCorrection(input, params.gamma); break; case ColorCorrectionMethod::LEVELS: - result = adjustLevels(input, params.blackPoint, params.whitePoint, params.gamma); + result = adjustLevels(input, params.blackPoint, params.whitePoint, + params.gamma); break; default: // Basic brightness/contrast as fallback @@ -363,11 +378,9 @@ blob ImageEnhancement::colorCorrection(const blob& input, } // Implement sharpen -blob ImageEnhancement::sharpen(const blob& input, - double strength, - double radius, - double threshold, - const std::string& method) const { +blob ImageEnhancement::sharpen(const blob& input, double strength, + double radius, double threshold, + const std::string& method) const { if (input.isEmpty()) { return blob{}; } @@ -381,8 +394,9 @@ blob ImageEnhancement::sharpen(const blob& input, if (method == "unsharp_mask") { cv::Mat blurred; - int ksize = static_cast(radius * 6 + 1); // Approximate Gaussian kernel - ksize = std::max(3, ksize | 1); // Make odd + int ksize = + static_cast(radius * 6 + 1); // Approximate Gaussian kernel + ksize = std::max(3, ksize | 1); // Make odd cv::GaussianBlur(src, blurred, cv::Size(ksize, ksize), radius); cv::addWeighted(src, 1.0 + strength, blurred, -strength, 0, dst); } else if (method == "high_pass") { @@ -418,11 +432,16 @@ blob ImageEnhancement::sharpen(const blob& input, for (int dy = -1; dy <= 1; ++dy) { for (int dx = -1; dx <= 1; ++dx) { int idx = (y + dy) * input.getWidth() + (x + dx); - sum += static_cast(result[idx]) * kernel[(dy + 1) * 3 + (dx + 1)]; + sum += static_cast(result[idx]) * + kernel[(dy + 1) * 3 + (dx + 1)]; } } - int val = static_cast(input[y * input.getWidth() + x]) + strength * (sum - 5 * static_cast(input[y * input.getWidth() + x])); - result[y * input.getWidth() + x] = static_cast(std::clamp(val, 0, 255)); + int val = + static_cast(input[y * input.getWidth() + x]) + + strength * (sum - 5 * static_cast( + input[y * input.getWidth() + x])); + result[y * input.getWidth() + x] = + static_cast(std::clamp(val, 0, 255)); } } } @@ -431,10 +450,9 @@ blob ImageEnhancement::sharpen(const blob& input, } // Implement denoise -blob ImageEnhancement::denoise(const blob& input, - double strength, - const std::string& method, - bool preserveEdges) const { +blob ImageEnhancement::denoise(const blob& input, double strength, + const std::string& method, + bool preserveEdges) const { if (input.isEmpty()) { return blob{}; } @@ -451,7 +469,9 @@ blob ImageEnhancement::denoise(const blob& input, cv::bilateralFilter(src, dst, d, sigmaColor, sigmaSpace); } else if (method == "nlm") { cv::Mat denoised; - cv::fastNlMeansDenoisingColored(src, denoised, static_cast(strength * 10), static_cast(strength * 10), 7, 21); + cv::fastNlMeansDenoisingColored( + src, denoised, static_cast(strength * 10), + static_cast(strength * 10), 7, 21); dst = denoised; } else if (method == "bm3d") { // OpenCV has no built-in BM3D, fallback to bilateral @@ -473,14 +493,16 @@ blob ImageEnhancement::denoise(const blob& input, // Manual bilateral-like filter blob result = input.clone(); int kernelSize = static_cast(5 + 2 * strength * 10); - kernelSize |= 1; // Odd + kernelSize |= 1; // Odd // Simplified averaging filter as fallback - for (int y = kernelSize/2; y < input.getHeight() - kernelSize/2; ++y) { - for (int x = kernelSize/2; x < input.getWidth() - kernelSize/2; ++x) { + for (int y = kernelSize / 2; y < input.getHeight() - kernelSize / 2; ++y) { + for (int x = kernelSize / 2; x < input.getWidth() - kernelSize / 2; + ++x) { int sum = 0; - for (int dy = -kernelSize/2; dy <= kernelSize/2; ++dy) { - for (int dx = -kernelSize/2; dx <= kernelSize/2; ++dx) { - sum += static_cast(input[(y + dy) * input.getWidth() + (x + dx)]); + for (int dy = -kernelSize / 2; dy <= kernelSize / 2; ++dy) { + for (int dx = -kernelSize / 2; dx <= kernelSize / 2; ++dx) { + sum += static_cast( + input[(y + dy) * input.getWidth() + (x + dx)]); } } int avg = sum / (kernelSize * kernelSize); @@ -492,10 +514,8 @@ blob ImageEnhancement::denoise(const blob& input, } // Implement shadowHighlight -blob ImageEnhancement::shadowHighlight(const blob& input, - double shadows, - double highlights, - double radius) const { +blob ImageEnhancement::shadowHighlight(const blob& input, double shadows, + double highlights, double radius) const { if (input.isEmpty()) { return blob{}; } @@ -521,7 +541,7 @@ blob ImageEnhancement::shadowHighlight(const blob& input, // Blend cv::Mat blendedL = lChannel.clone(); - blendedL = shadowsL * 0.5 + highlightsL * 0.5; // Simple blend + blendedL = shadowsL * 0.5 + highlightsL * 0.5; // Simple blend channels[0] = blendedL; cv::merge(channels, lab); @@ -533,27 +553,29 @@ blob ImageEnhancement::shadowHighlight(const blob& input, blob result = input.clone(); double shadowLift = shadows / 100.0 * 50; double highlightCompress = highlights / 100.0 * 50; - for (size_t i = 0; i < result.size(); i += 3) { // Assume RGB - std::array pixel = {static_cast(result[i]), static_cast(result[i+1]), static_cast(result[i+2])}; + for (size_t i = 0; i < result.size(); i += 3) { // Assume RGB + std::array pixel = {static_cast(result[i]), + static_cast(result[i + 1]), + static_cast(result[i + 2])}; int avg = (pixel[0] + pixel[1] + pixel[2]) / 3; if (avg < 50) { - for (auto& p : pixel) p = std::min(255, p + static_cast(shadowLift)); + for (auto& p : pixel) + p = std::min(255, p + static_cast(shadowLift)); } else if (avg > 200) { - for (auto& p : pixel) p = std::max(0, p - static_cast(highlightCompress)); + for (auto& p : pixel) + p = std::max(0, p - static_cast(highlightCompress)); } result[i] = static_cast(pixel[0]); - result[i+1] = static_cast(pixel[1]); - result[i+2] = static_cast(pixel[2]); + result[i + 1] = static_cast(pixel[1]); + result[i + 2] = static_cast(pixel[2]); } return result; #endif } // Implement clarity -blob ImageEnhancement::clarity(const blob& input, - double clarity, - double radius, - bool preserveSkin) const { +blob ImageEnhancement::clarity(const blob& input, double clarity, double radius, + bool preserveSkin) const { if (input.isEmpty()) { return blob{}; } @@ -564,10 +586,10 @@ blob ImageEnhancement::clarity(const blob& input, cv::Mat blurred, highPass, dst; cv::GaussianBlur(src, blurred, cv::Size(0, 0), radius); cv::subtract(src, blurred, highPass); - + // Apply clarity amount cv::addWeighted(src, 1.0, highPass, clarity / 100.0, 0, dst); - + if (preserveSkin) { // Simple skin tone preservation: reduce clarity on warm tones cv::Mat hsv; @@ -576,9 +598,10 @@ blob ImageEnhancement::clarity(const blob& input, cv::split(hsv, channels); // Reduce clarity where hue is skin-like (20-40) cv::Mat skinMask = (channels[0] > 20) & (channels[0] < 40); - dst.setTo(cv::Scalar(0,0,0), skinMask); // Placeholder: blend back original + dst.setTo(cv::Scalar(0, 0, 0), + skinMask); // Placeholder: blend back original } - + return blob(dst); #else // Manual high-pass clarity @@ -592,13 +615,19 @@ blob ImageEnhancement::clarity(const blob& input, int avg = 0; for (int dy = -1; dy <= 1; ++dy) { for (int dx = -1; dx <= 1; ++dx) { - if (dy == 0 && dx == 0) continue; - avg += static_cast(input[(y + dy) * input.getWidth() + (x + dx)]); + if (dy == 0 && dx == 0) + continue; + avg += static_cast( + input[(y + dy) * input.getWidth() + (x + dx)]); } } avg /= 8; - int enhanced = static_cast(input[y * input.getWidth() + x]) + amount * (static_cast(input[y * input.getWidth() + x]) - avg); - result[y * input.getWidth() + x] = static_cast(std::clamp(enhanced, 0, 255)); + int enhanced = + static_cast(input[y * input.getWidth() + x]) + + amount * + (static_cast(input[y * input.getWidth() + x]) - avg); + result[y * input.getWidth() + x] = + static_cast(std::clamp(enhanced, 0, 255)); } } return result; @@ -606,9 +635,8 @@ blob ImageEnhancement::clarity(const blob& input, } // Implement dehaze -blob ImageEnhancement::dehaze(const blob& input, - double strength, - bool preserveColors) const { +blob ImageEnhancement::dehaze(const blob& input, double strength, + bool preserveColors) const { if (input.isEmpty()) { return blob{}; } @@ -617,7 +645,7 @@ blob ImageEnhancement::dehaze(const blob& input, #ifdef ATOM_IMAGE_HAS_OPENCV cv::Mat src = input.to_mat(); cv::Mat dst; - + // Simple dark channel prior approximation cv::Mat darkChannel = cv::Mat::ones(src.size(), src.type()) * 255; std::vector channels; @@ -625,34 +653,36 @@ blob ImageEnhancement::dehaze(const blob& input, for (int i = 0; i < 3; ++i) { cv::min(darkChannel, channels[i], darkChannel); } - + // Estimate airlight (simplified: top 0.1% brightest) cv::Mat sortedDark; cv::sort(darkChannel, sortedDark, cv::SORT_EVERY_ROW + cv::SORT_DESCENDING); - cv::Scalar airlight = cv::mean(sortedDark(cv::Rect(0, 0, 1, static_cast(sortedDark.rows * 0.001)))); - + cv::Scalar airlight = cv::mean(sortedDark( + cv::Rect(0, 0, 1, static_cast(sortedDark.rows * 0.001)))); + // Transmission map (simplified) double t = 1.0 - strength * 0.95; cv::Mat transmission = darkChannel / std::max(airlight[0], 1.0); transmission = transmission * t + (1 - t); - + // Dehaze formula cv::Mat normalized = src / 255.0; - dst = (normalized - airlight[0] / 255.0) / transmission + airlight[0] / 255.0; + dst = + (normalized - airlight[0] / 255.0) / transmission + airlight[0] / 255.0; dst *= 255.0; dst.convertTo(dst, CV_8U); - + if (preserveColors) { // Adjust saturation cv::Mat hsv; cv::cvtColor(dst, hsv, cv::COLOR_BGR2HSV); std::vector hsvChannels; cv::split(hsv, hsvChannels); - hsvChannels[1] *= preserveColors ? 0.8 : 1.0; // Reduce saturation + hsvChannels[1] *= preserveColors ? 0.8 : 1.0; // Reduce saturation cv::merge(hsvChannels, hsv); cv::cvtColor(hsv, dst, cv::COLOR_HSV2BGR); } - + return blob(dst); #else // Manual simple dehaze: increase contrast and brightness @@ -661,11 +691,10 @@ blob ImageEnhancement::dehaze(const blob& input, } // Implement calculateHistogram -std::vector> ImageEnhancement::calculateHistogram(const blob& input, - int channel, - int bins) const { +std::vector> ImageEnhancement::calculateHistogram( + const blob& input, int channel, int bins) const { std::vector> histograms; - + if (input.isEmpty()) { return histograms; } @@ -682,7 +711,8 @@ std::vector> ImageEnhancement::calculateHistogram(const blob int histSize = bins; float range[] = {0, 256}; const float* histRange = {range}; - cv::calcHist(&channels[i], 1, 0, cv::Mat(), hist, 1, &histSize, &histRange); + cv::calcHist(&channels[i], 1, 0, cv::Mat(), hist, 1, &histSize, + &histRange); histograms[i].resize(bins); for (int j = 0; j < bins; ++j) { histograms[i][j] = hist.at(j); @@ -695,7 +725,8 @@ std::vector> ImageEnhancement::calculateHistogram(const blob int histSize = bins; float range[] = {0, 256}; const float* histRange = {range}; - cv::calcHist(&src, 1, &channel, cv::Mat(), hist, 1, &histSize, &histRange); + cv::calcHist(&src, 1, &channel, cv::Mat(), hist, 1, &histSize, + &histRange); histograms[0].resize(bins); for (int j = 0; j < bins; ++j) { histograms[0][j] = hist.at(j); @@ -718,14 +749,14 @@ std::vector> ImageEnhancement::calculateHistogram(const blob } histograms.push_back(hist); #endif - + return histograms; } // Implement applyCurve -blob ImageEnhancement::applyCurve(const blob& input, - const std::vector>& curve, - int channel) const { +blob ImageEnhancement::applyCurve( + const blob& input, const std::vector>& curve, + int channel) const { if (input.isEmpty() || curve.empty()) { return input; } @@ -733,19 +764,23 @@ blob ImageEnhancement::applyCurve(const blob& input, #ifdef ATOM_IMAGE_HAS_OPENCV cv::Mat src = input.to_mat(); cv::Mat dst = src.clone(); - + // Create lookup table from curve points (linear interpolation) std::vector points; for (const auto& p : curve) { - points.emplace_back(static_cast(p.first), static_cast(p.second)); + points.emplace_back(static_cast(p.first), + static_cast(p.second)); } cv::Mat lut(1, 256, CV_8U); - + // Interpolate curve for (int i = 0; i < 256; ++i) { // Find segment - auto it = std::lower_bound(points.begin(), points.end(), cv::Point2f(static_cast(i), 0), - [](const cv::Point2f& a, const cv::Point2f& b) { return a.x < b.x; }); + auto it = std::lower_bound( + points.begin(), points.end(), cv::Point2f(static_cast(i), 0), + [](const cv::Point2f& a, const cv::Point2f& b) { + return a.x < b.x; + }); if (it == points.end()) { lut.at(0, i) = static_cast(points.back().y); } else if (it == points.begin()) { @@ -754,10 +789,11 @@ blob ImageEnhancement::applyCurve(const blob& input, auto prev = std::prev(it); float t = (i - prev->x) / (it->x - prev->x); float val = prev->y * (1 - t) + it->y * t; - lut.at(0, i) = static_cast(std::clamp(val, 0.0f, 255.0f)); + lut.at(0, i) = + static_cast(std::clamp(val, 0.0f, 255.0f)); } } - + if (channel == -1) { // Apply to all channels std::vector channels; @@ -775,7 +811,7 @@ blob ImageEnhancement::applyCurve(const blob& input, } cv::merge(channels, dst); } - + return blob(dst); #else // Manual curve application @@ -784,8 +820,10 @@ blob ImageEnhancement::applyCurve(const blob& input, std::array lut{}; for (int i = 0; i < 256; ++i) { // Simple linear interpolation between points - auto it = std::lower_bound(curve.begin(), curve.end(), std::make_pair(static_cast(i), -1.0), - [](const auto& a, const auto& b) { return a.first < b.first; }); + auto it = std::lower_bound( + curve.begin(), curve.end(), + std::make_pair(static_cast(i), -1.0), + [](const auto& a, const auto& b) { return a.first < b.first; }); if (it == curve.end()) { lut[i] = static_cast(curve.back().second); } else if (it == curve.begin()) { @@ -797,7 +835,7 @@ blob ImageEnhancement::applyCurve(const blob& input, lut[i] = static_cast(std::clamp(val, 0.0, 255.0)); } } - + // Apply LUT (assume single channel for simplicity) for (size_t i = 0; i < result.size(); ++i) { uint8_t val = static_cast(result[i]); @@ -808,12 +846,10 @@ blob ImageEnhancement::applyCurve(const blob& input, } // Implement adjustLevels -blob ImageEnhancement::adjustLevels(const blob& input, - double blackPoint, - double whitePoint, - double gamma, - double outputBlack, - double outputWhite) const { +blob ImageEnhancement::adjustLevels(const blob& input, double blackPoint, + double whitePoint, double gamma, + double outputBlack, + double outputWhite) const { if (input.isEmpty()) { return input; } @@ -821,27 +857,27 @@ blob ImageEnhancement::adjustLevels(const blob& input, #ifdef ATOM_IMAGE_HAS_OPENCV cv::Mat src = input.to_mat(); cv::Mat dst; - + // Normalize to 0-1 cv::Mat norm; - src.convertTo(norm, CV_32F, 1.0/255.0); - + src.convertTo(norm, CV_32F, 1.0 / 255.0); + // Stretch to [blackPoint, whitePoint] double inputRange = whitePoint - blackPoint; norm = (norm - blackPoint / 255.0) / (inputRange / 255.0); norm = cv::max(norm, 0.0); norm = cv::min(norm, 1.0); - + // Apply gamma cv::pow(norm, gamma, norm); - + // Output range double outputRange = outputWhite - outputBlack; norm = norm * (outputRange / 255.0) + outputBlack / 255.0; - + // Back to 8-bit norm.convertTo(dst, CV_8U, 255.0); - + return blob(dst); #else // Manual levels adjustment @@ -852,7 +888,7 @@ blob ImageEnhancement::adjustLevels(const blob& input, double ob = outputBlack / 255.0; double ow = outputWhite / 255.0; double scale = ow - ob; - + for (size_t i = 0; i < result.size(); ++i) { double val = static_cast(result[i]) / 255.0; val = std::max(0.0, (val - bp) / (wp - bp)); @@ -867,62 +903,79 @@ blob ImageEnhancement::adjustLevels(const blob& input, // Implement protected enhanceInColorSpace std::vector ImageEnhancement::enhanceInColorSpace( - const std::vector& input, - int width, int height, int channels, + const std::vector& input, int width, int height, int channels, ColorSpace colorSpace, - std::function(const std::vector&, int, int, int)> enhanceFunction) const { + std::function(const std::vector&, int, + int, int)> + enhanceFunction) const { if (input.empty() || width <= 0 || height <= 0 || channels <= 0) { return input; } #ifdef ATOM_IMAGE_HAS_OPENCV // Use OpenCV for color space conversion - cv::Mat rgb(height, width, CV_8UC(channels), const_cast(input.data())); + cv::Mat rgb(height, width, CV_8UC(channels), + const_cast(input.data())); cv::Mat converted; - + int code = -1; - if (colorSpace == ColorSpace::HSV) code = cv::COLOR_RGB2HSV; - else if (colorSpace == ColorSpace::HSL) code = cv::COLOR_RGB2HLS; // Approximate - else if (colorSpace == ColorSpace::LAB) code = cv::COLOR_RGB2Lab; - else if (colorSpace == ColorSpace::YUV) code = cv::COLOR_RGB2YUV; - else if (colorSpace == ColorSpace::XYZ) code = cv::COLOR_RGB2XYZ; + if (colorSpace == ColorSpace::HSV) + code = cv::COLOR_RGB2HSV; + else if (colorSpace == ColorSpace::HSL) + code = cv::COLOR_RGB2HLS; // Approximate + else if (colorSpace == ColorSpace::LAB) + code = cv::COLOR_RGB2Lab; + else if (colorSpace == ColorSpace::YUV) + code = cv::COLOR_RGB2YUV; + else if (colorSpace == ColorSpace::XYZ) + code = cv::COLOR_RGB2XYZ; else if (colorSpace == ColorSpace::GRAY) { cv::cvtColor(rgb, converted, cv::COLOR_RGB2GRAY); channels = 1; } - + if (code != -1) { cv::cvtColor(rgb, converted, code); } else { converted = rgb; } - + // Convert cv::Mat to std::vector for the enhancement function const auto* byte_ptr = reinterpret_cast(converted.data); - std::vector input_data(byte_ptr, byte_ptr + converted.total() * converted.elemSize()); + std::vector input_data( + byte_ptr, byte_ptr + converted.total() * converted.elemSize()); // Apply enhancement - std::vector enhancedData(enhanceFunction(input_data, converted.rows, converted.cols, converted.channels())); - + std::vector enhancedData(enhanceFunction( + input_data, converted.rows, converted.cols, converted.channels())); + // Convert back cv::Mat enhanced(height, width, CV_8UC(channels), enhancedData.data()); cv::Mat result; int backCode = -1; - if (colorSpace == ColorSpace::HSV) backCode = cv::COLOR_HSV2RGB; - else if (colorSpace == ColorSpace::HSL) backCode = cv::COLOR_HLS2RGB; - else if (colorSpace == ColorSpace::LAB) backCode = cv::COLOR_Lab2RGB; - else if (colorSpace == ColorSpace::YUV) backCode = cv::COLOR_YUV2RGB; - else if (colorSpace == ColorSpace::XYZ) backCode = cv::COLOR_XYZ2RGB; - else if (colorSpace == ColorSpace::GRAY) backCode = cv::COLOR_GRAY2RGB; - + if (colorSpace == ColorSpace::HSV) + backCode = cv::COLOR_HSV2RGB; + else if (colorSpace == ColorSpace::HSL) + backCode = cv::COLOR_HLS2RGB; + else if (colorSpace == ColorSpace::LAB) + backCode = cv::COLOR_Lab2RGB; + else if (colorSpace == ColorSpace::YUV) + backCode = cv::COLOR_YUV2RGB; + else if (colorSpace == ColorSpace::XYZ) + backCode = cv::COLOR_XYZ2RGB; + else if (colorSpace == ColorSpace::GRAY) + backCode = cv::COLOR_GRAY2RGB; + if (backCode != -1) { cv::cvtColor(enhanced, result, backCode); } else { result = enhanced; } - - const auto* result_byte_ptr = reinterpret_cast(result.data); - return std::vector(result_byte_ptr, result_byte_ptr + result.total() * result.elemSize()); + + const auto* result_byte_ptr = + reinterpret_cast(result.data); + return std::vector( + result_byte_ptr, result_byte_ptr + result.total() * result.elemSize()); #else // Manual conversion and enhancement (simplified for RGB only) return enhanceFunction(input, width, height, channels); @@ -930,10 +983,10 @@ std::vector ImageEnhancement::enhanceInColorSpace( } // Implement rgbToColorSpace -std::array ImageEnhancement::rgbToColorSpace(const std::array& rgb, - ColorSpace colorSpace) const { +std::array ImageEnhancement::rgbToColorSpace( + const std::array& rgb, ColorSpace colorSpace) const { double r = rgb[0] / 255.0, g = rgb[1] / 255.0, b = rgb[2] / 255.0; - + switch (colorSpace) { case ColorSpace::HSV: { double maxC = std::max({r, g, b}); @@ -941,9 +994,12 @@ std::array ImageEnhancement::rgbToColorSpace(const std::array 0) { - if (maxC == r) h = 60 * fmod((g - b) / delta, 6); - else if (maxC == g) h = 60 * ((b - r) / delta + 2); - else h = 60 * ((r - g) / delta + 4); + if (maxC == r) + h = 60 * fmod((g - b) / delta, 6); + else if (maxC == g) + h = 60 * ((b - r) / delta + 2); + else + h = 60 * ((r - g) / delta + 4); } double s = maxC == 0 ? 0 : delta / maxC; return {h, s, maxC}; @@ -954,11 +1010,16 @@ std::array ImageEnhancement::rgbToColorSpace(const std::array 0) { - if (l <= 0.5) s = delta / (maxC + minC); - else s = delta / (2 - maxC - minC); - if (maxC == r) h = fmod((g - b) / delta + 6, 6) * 60; - else if (maxC == g) h = ((b - r) / delta + 2) * 60; - else h = ((r - g) / delta + 4) * 60; + if (l <= 0.5) + s = delta / (maxC + minC); + else + s = delta / (2 - maxC - minC); + if (maxC == r) + h = fmod((g - b) / delta + 6, 6) * 60; + else if (maxC == g) + h = ((b - r) / delta + 2) * 60; + else + h = ((r - g) / delta + 4) * 60; } return {h, s, l}; } @@ -967,9 +1028,12 @@ std::array ImageEnhancement::rgbToColorSpace(const std::array 0.008856 ? pow(x, 1/3.0) : 7.787 * x + 16.0/116.0; - double fy = y > 0.008856 ? pow(y, 1/3.0) : 7.787 * y + 16.0/116.0; - double fz = z > 0.008856 ? pow(z, 1/3.0) : 7.787 * z + 16.0/116.0; + double fx = + x > 0.008856 ? pow(x, 1 / 3.0) : 7.787 * x + 16.0 / 116.0; + double fy = + y > 0.008856 ? pow(y, 1 / 3.0) : 7.787 * y + 16.0 / 116.0; + double fz = + z > 0.008856 ? pow(z, 1 / 3.0) : 7.787 * z + 16.0 / 116.0; double l = 116 * fy - 16; double a = 500 * (fx - fy); double b_ = 200 * (fy - fz); @@ -988,15 +1052,15 @@ std::array ImageEnhancement::rgbToColorSpace(const std::array ImageEnhancement::colorSpaceToRgb(const std::array& values, - ColorSpace colorSpace) const { +std::array ImageEnhancement::colorSpaceToRgb( + const std::array& values, ColorSpace colorSpace) const { double r = 0, g = 0, b = 0; - + switch (colorSpace) { case ColorSpace::HSV: { double h = values[0], s = values[1], v = values[2]; @@ -1007,12 +1071,36 @@ std::array ImageEnhancement::colorSpaceToRgb(const std::array ImageEnhancement::colorSpaceToRgb(const std::array ImageEnhancement::colorSpaceToRgb(const std::array 0.2069 ? fx*fx*fx : (fx - 16.0/116.0) / 7.787; - double y = fy > 0.2069 ? fy*fy*fy : (fy - 16.0/116.0) / 7.787; - double z = fz > 0.2069 ? fz*fz*fz : (fz - 16.0/116.0) / 7.787; + double x = fx > 0.2069 ? fx * fx * fx : (fx - 16.0 / 116.0) / 7.787; + double y = fy > 0.2069 ? fy * fy * fy : (fy - 16.0 / 116.0) / 7.787; + double z = fz > 0.2069 ? fz * fz * fz : (fz - 16.0 / 116.0) / 7.787; double rr = 3.2406 * x - 1.5372 * y - 0.4986 * z; double gg = -0.9689 * x + 1.8758 * y + 0.0415 * z; double bb_ = 0.0557 * x - 0.2040 * y + 1.0570 * z; - r = rr > 0.0031308 ? 1.055 * pow(rr, 1/2.4) - 0.055 : 12.92 * rr; - g = gg > 0.0031308 ? 1.055 * pow(gg, 1/2.4) - 0.055 : 12.92 * gg; - b = bb_ > 0.0031308 ? 1.055 * pow(bb_, 1/2.4) - 0.055 : 12.92 * bb_; + r = rr > 0.0031308 ? 1.055 * pow(rr, 1 / 2.4) - 0.055 : 12.92 * rr; + g = gg > 0.0031308 ? 1.055 * pow(gg, 1 / 2.4) - 0.055 : 12.92 * gg; + b = bb_ > 0.0031308 ? 1.055 * pow(bb_, 1 / 2.4) - 0.055 + : 12.92 * bb_; break; } case ColorSpace::YUV: { @@ -1058,15 +1147,17 @@ std::array ImageEnhancement::colorSpaceToRgb(const std::array 0.0031308 ? 1.055 * pow(r, 1/2.4) - 0.055 : 12.92 * r; - g = g > 0.0031308 ? 1.055 * pow(g, 1/2.4) - 0.055 : 12.92 * g; - b = b > 0.0031308 ? 1.055 * pow(b, 1/2.4) - 0.055 : 12.92 * b; + r = r > 0.0031308 ? 1.055 * pow(r, 1 / 2.4) - 0.055 : 12.92 * r; + g = g > 0.0031308 ? 1.055 * pow(g, 1 / 2.4) - 0.055 : 12.92 * g; + b = b > 0.0031308 ? 1.055 * pow(b, 1 / 2.4) - 0.055 : 12.92 * b; break; } default: - r = values[0]; g = values[1]; b = values[2]; + r = values[0]; + g = values[1]; + b = values[2]; } - + return {static_cast(std::clamp(r * 255, 0.0, 255.0)), static_cast(std::clamp(g * 255, 0.0, 255.0)), static_cast(std::clamp(b * 255, 0.0, 255.0))}; @@ -1074,17 +1165,22 @@ std::array ImageEnhancement::colorSpaceToRgb(const std::array 1) t -= 1; - if (t < 1.0/6) return p + (q - p) * 6 * t; - if (t < 0.5) return q; - if (t < 2.0/6) return p + (q - p) * (2.0/3 - t) * 6; + if (t < 0) + t += 1; + if (t > 1) + t -= 1; + if (t < 1.0 / 6) + return p + (q - p) * 6 * t; + if (t < 0.5) + return q; + if (t < 2.0 / 6) + return p + (q - p) * (2.0 / 3 - t) * 6; return p; } // Implement factory std::unique_ptr createOptimalEnhancement(bool useGPU) { - (void)useGPU; // GPU not implemented yet + (void)useGPU; // GPU not implemented yet return std::make_unique(); } diff --git a/atom/image/processing/enhancement.hpp b/atom/image/processing/enhancement.hpp index 8f139b62..59aed648 100644 --- a/atom/image/processing/enhancement.hpp +++ b/atom/image/processing/enhancement.hpp @@ -14,11 +14,11 @@ * @version 1.0.0 */ -#include "../core/image_blob.hpp" -#include #include -#include #include +#include +#include +#include "../core/image_blob.hpp" namespace atom::image { @@ -26,52 +26,52 @@ namespace atom::image { * @brief Color space types for enhancement operations */ enum class ColorSpace { - RGB, // Red, Green, Blue - HSV, // Hue, Saturation, Value - HSL, // Hue, Saturation, Lightness - LAB, // L*a*b* color space - YUV, // Luminance, Chrominance - XYZ, // CIE XYZ - GRAY // Grayscale + RGB, // Red, Green, Blue + HSV, // Hue, Saturation, Value + HSL, // Hue, Saturation, Lightness + LAB, // L*a*b* color space + YUV, // Luminance, Chrominance + XYZ, // CIE XYZ + GRAY // Grayscale }; /** * @brief Histogram equalization methods */ enum class HistogramMethod { - GLOBAL, // Global histogram equalization - ADAPTIVE, // Adaptive histogram equalization (AHE) - CLAHE, // Contrast Limited AHE - LOCAL, // Local histogram equalization - MULTI_SCALE // Multi-scale histogram equalization + GLOBAL, // Global histogram equalization + ADAPTIVE, // Adaptive histogram equalization (AHE) + CLAHE, // Contrast Limited AHE + LOCAL, // Local histogram equalization + MULTI_SCALE // Multi-scale histogram equalization }; /** * @brief Tone mapping operators */ enum class ToneMappingOperator { - REINHARD, // Reinhard tone mapping - DRAGO, // Drago tone mapping - MANTIUK, // Mantiuk tone mapping - FATTAL, // Fattal tone mapping - DURAND, // Durand tone mapping - GAMMA, // Simple gamma correction - LINEAR, // Linear tone mapping - LOGARITHMIC // Logarithmic tone mapping + REINHARD, // Reinhard tone mapping + DRAGO, // Drago tone mapping + MANTIUK, // Mantiuk tone mapping + FATTAL, // Fattal tone mapping + DURAND, // Durand tone mapping + GAMMA, // Simple gamma correction + LINEAR, // Linear tone mapping + LOGARITHMIC // Logarithmic tone mapping }; /** * @brief Color correction methods */ enum class ColorCorrectionMethod { - WHITE_BALANCE, // White balance correction - COLOR_CAST, // Color cast removal - GAMMA_CORRECTION, // Gamma correction - CURVES, // Tone curves adjustment - LEVELS, // Levels adjustment - COLOR_GRADING, // Professional color grading - AUTO_LEVELS, // Automatic levels adjustment - AUTO_COLOR // Automatic color correction + WHITE_BALANCE, // White balance correction + COLOR_CAST, // Color cast removal + GAMMA_CORRECTION, // Gamma correction + CURVES, // Tone curves adjustment + LEVELS, // Levels adjustment + COLOR_GRADING, // Professional color grading + AUTO_LEVELS, // Automatic levels adjustment + AUTO_COLOR // Automatic color correction }; /** @@ -79,35 +79,35 @@ enum class ColorCorrectionMethod { */ struct EnhancementParams { // Histogram parameters - double clipLimit = 2.0; // CLAHE clip limit - int tileGridSize = 8; // CLAHE tile grid size + double clipLimit = 2.0; // CLAHE clip limit + int tileGridSize = 8; // CLAHE tile grid size // Tone mapping parameters - double gamma = 2.2; // Gamma value - double exposure = 0.0; // Exposure adjustment - double saturation = 1.0; // Saturation multiplier - double intensity = 1.0; // Intensity multiplier - double lightAdaptation = 1.0; // Light adaptation - double colorAdaptation = 0.0; // Color adaptation + double gamma = 2.2; // Gamma value + double exposure = 0.0; // Exposure adjustment + double saturation = 1.0; // Saturation multiplier + double intensity = 1.0; // Intensity multiplier + double lightAdaptation = 1.0; // Light adaptation + double colorAdaptation = 0.0; // Color adaptation // Color correction parameters - double temperature = 6500.0; // Color temperature (K) - double tint = 0.0; // Tint adjustment - std::array whitePoint = {1.0, 1.0, 1.0}; // White point - std::array blackPoint = {0.0, 0.0, 0.0}; // Black point + double temperature = 6500.0; // Color temperature (K) + double tint = 0.0; // Tint adjustment + std::array whitePoint = {1.0, 1.0, 1.0}; // White point + std::array blackPoint = {0.0, 0.0, 0.0}; // Black point // Contrast and brightness - double contrast = 1.0; // Contrast multiplier - double brightness = 0.0; // Brightness offset - double highlights = 0.0; // Highlights adjustment - double shadows = 0.0; // Shadows adjustment - double midtones = 0.0; // Midtones adjustment + double contrast = 1.0; // Contrast multiplier + double brightness = 0.0; // Brightness offset + double highlights = 0.0; // Highlights adjustment + double shadows = 0.0; // Shadows adjustment + double midtones = 0.0; // Midtones adjustment // Advanced parameters - double vibrance = 0.0; // Vibrance adjustment - double clarity = 0.0; // Clarity/structure enhancement - double dehaze = 0.0; // Dehaze strength - bool preserveLuminance = true; // Preserve luminance during color ops + double vibrance = 0.0; // Vibrance adjustment + double clarity = 0.0; // Clarity/structure enhancement + double dehaze = 0.0; // Dehaze strength + bool preserveLuminance = true; // Preserve luminance during color ops }; /** @@ -125,9 +125,9 @@ class ImageEnhancement { * @param params Enhancement parameters * @return Enhanced image blob */ - virtual blob equalizeHistogram(const blob& input, - HistogramMethod method = HistogramMethod::CLAHE, - const EnhancementParams& params = {}) const; + virtual blob equalizeHistogram( + const blob& input, HistogramMethod method = HistogramMethod::CLAHE, + const EnhancementParams& params = {}) const; /** * @brief Apply tone mapping for HDR images @@ -136,9 +136,10 @@ class ImageEnhancement { * @param params Tone mapping parameters * @return Tone-mapped LDR image blob */ - virtual blob toneMapping(const blob& input, - ToneMappingOperator operator_ = ToneMappingOperator::REINHARD, - const EnhancementParams& params = {}) const; + virtual blob toneMapping( + const blob& input, + ToneMappingOperator operator_ = ToneMappingOperator::REINHARD, + const EnhancementParams& params = {}) const; /** * @brief Apply color correction @@ -147,9 +148,10 @@ class ImageEnhancement { * @param params Correction parameters * @return Color-corrected image blob */ - virtual blob colorCorrection(const blob& input, - ColorCorrectionMethod method = ColorCorrectionMethod::AUTO_COLOR, - const EnhancementParams& params = {}) const; + virtual blob colorCorrection( + const blob& input, + ColorCorrectionMethod method = ColorCorrectionMethod::AUTO_COLOR, + const EnhancementParams& params = {}) const; /** * @brief Adjust brightness and contrast @@ -160,9 +162,9 @@ class ImageEnhancement { * @return Adjusted image blob */ virtual blob adjustBrightnessContrast(const blob& input, - double brightness = 0.0, - double contrast = 1.0, - bool preserveDetails = true) const; + double brightness = 0.0, + double contrast = 1.0, + bool preserveDetails = true) const; /** * @brief Apply gamma correction @@ -171,9 +173,8 @@ class ImageEnhancement { * @param colorSpace Color space for gamma correction * @return Gamma-corrected image blob */ - virtual blob gammaCorrection(const blob& input, - double gamma = 2.2, - ColorSpace colorSpace = ColorSpace::RGB) const; + virtual blob gammaCorrection(const blob& input, double gamma = 2.2, + ColorSpace colorSpace = ColorSpace::RGB) const; /** * @brief Enhance image sharpness @@ -184,11 +185,9 @@ class ImageEnhancement { * @param method Sharpening method ("unsharp_mask", "high_pass", "clarity") * @return Sharpened image blob */ - virtual blob sharpen(const blob& input, - double strength = 1.0, - double radius = 1.0, - double threshold = 0.0, - const std::string& method = "unsharp_mask") const; + virtual blob sharpen(const blob& input, double strength = 1.0, + double radius = 1.0, double threshold = 0.0, + const std::string& method = "unsharp_mask") const; /** * @brief Reduce image noise @@ -198,10 +197,9 @@ class ImageEnhancement { * @param preserveEdges Whether to preserve edges * @return Denoised image blob */ - virtual blob denoise(const blob& input, - double strength = 0.5, - const std::string& method = "bilateral", - bool preserveEdges = true) const; + virtual blob denoise(const blob& input, double strength = 0.5, + const std::string& method = "bilateral", + bool preserveEdges = true) const; /** * @brief Apply shadow/highlight adjustment @@ -211,10 +209,9 @@ class ImageEnhancement { * @param radius Adjustment radius * @return Adjusted image blob */ - virtual blob shadowHighlight(const blob& input, - double shadows = 0.0, - double highlights = 0.0, - double radius = 30.0) const; + virtual blob shadowHighlight(const blob& input, double shadows = 0.0, + double highlights = 0.0, + double radius = 30.0) const; /** * @brief Apply vibrance and saturation adjustment @@ -223,9 +220,8 @@ class ImageEnhancement { * @param saturation Saturation adjustment (-100 to 100) * @return Adjusted image blob */ - virtual blob vibranceSaturation(const blob& input, - double vibrance = 0.0, - double saturation = 0.0) const; + virtual blob vibranceSaturation(const blob& input, double vibrance = 0.0, + double saturation = 0.0) const; /** * @brief Apply clarity/structure enhancement @@ -235,10 +231,8 @@ class ImageEnhancement { * @param preserveSkin Whether to preserve skin tones * @return Enhanced image blob */ - virtual blob clarity(const blob& input, - double clarity = 0.0, - double radius = 20.0, - bool preserveSkin = true) const; + virtual blob clarity(const blob& input, double clarity = 0.0, + double radius = 20.0, bool preserveSkin = true) const; /** * @brief Apply dehaze filter @@ -247,9 +241,8 @@ class ImageEnhancement { * @param preserveColors Whether to preserve color balance * @return Dehazed image blob */ - virtual blob dehaze(const blob& input, - double strength = 0.5, - bool preserveColors = true) const; + virtual blob dehaze(const blob& input, double strength = 0.5, + bool preserveColors = true) const; /** * @brief Apply automatic enhancement @@ -259,8 +252,8 @@ class ImageEnhancement { * @return Auto-enhanced image blob */ virtual blob autoEnhance(const blob& input, - const std::string& mode = "auto", - double strength = 0.8) const; + const std::string& mode = "auto", + double strength = 0.8) const; /** * @brief Convert between color spaces @@ -269,9 +262,8 @@ class ImageEnhancement { * @param toSpace Target color space * @return Converted image blob */ - virtual blob convertColorSpace(const blob& input, - ColorSpace fromSpace, - ColorSpace toSpace) const; + virtual blob convertColorSpace(const blob& input, ColorSpace fromSpace, + ColorSpace toSpace) const; /** * @brief Calculate image histogram @@ -280,9 +272,8 @@ class ImageEnhancement { * @param bins Number of histogram bins * @return Histogram data */ - virtual std::vector> calculateHistogram(const blob& input, - int channel = -1, - int bins = 256) const; + virtual std::vector> calculateHistogram( + const blob& input, int channel = -1, int bins = 256) const; /** * @brief Apply tone curve adjustment @@ -292,8 +283,8 @@ class ImageEnhancement { * @return Curve-adjusted image blob */ virtual blob applyCurve(const blob& input, - const std::vector>& curve, - int channel = -1) const; + const std::vector>& curve, + int channel = -1) const; /** * @brief Apply levels adjustment @@ -305,12 +296,10 @@ class ImageEnhancement { * @param outputWhite Output white point (0-255) * @return Levels-adjusted image blob */ - virtual blob adjustLevels(const blob& input, - double blackPoint = 0.0, - double whitePoint = 255.0, - double gamma = 1.0, - double outputBlack = 0.0, - double outputWhite = 255.0) const; + virtual blob adjustLevels(const blob& input, double blackPoint = 0.0, + double whitePoint = 255.0, double gamma = 1.0, + double outputBlack = 0.0, + double outputWhite = 255.0) const; protected: /** @@ -324,10 +313,11 @@ class ImageEnhancement { * @return Enhanced image data */ virtual std::vector enhanceInColorSpace( - const std::vector& input, - int width, int height, int channels, - ColorSpace colorSpace, - std::function(const std::vector&, int, int, int)> enhanceFunction) const; + const std::vector& input, int width, int height, + int channels, ColorSpace colorSpace, + std::function(const std::vector&, int, + int, int)> + enhanceFunction) const; /** * @brief Convert RGB to specified color space @@ -335,8 +325,8 @@ class ImageEnhancement { * @param colorSpace Target color space * @return Converted values */ - virtual std::array rgbToColorSpace(const std::array& rgb, - ColorSpace colorSpace) const; + virtual std::array rgbToColorSpace( + const std::array& rgb, ColorSpace colorSpace) const; /** * @brief Convert from specified color space to RGB @@ -344,8 +334,8 @@ class ImageEnhancement { * @param colorSpace Source color space * @return RGB values (0-255) */ - virtual std::array colorSpaceToRgb(const std::array& values, - ColorSpace colorSpace) const; + virtual std::array colorSpaceToRgb( + const std::array& values, ColorSpace colorSpace) const; /** * @brief Helper for HSL to RGB conversion @@ -360,6 +350,6 @@ class ImageEnhancement { */ std::unique_ptr createOptimalEnhancement(bool useGPU = false); -} // namespace atom::image +} // namespace atom::image -#endif // ATOM_IMAGE_ENHANCEMENT_HPP +#endif // ATOM_IMAGE_ENHANCEMENT_HPP diff --git a/atom/image/processing/filters.hpp b/atom/image/processing/filters.hpp index 82d47b35..f3c39c75 100644 --- a/atom/image/processing/filters.hpp +++ b/atom/image/processing/filters.hpp @@ -14,13 +14,13 @@ * @version 1.0.0 */ -#include "../core/image_blob.hpp" -#include #include #include #include #include #include +#include +#include "../core/image_blob.hpp" namespace atom::image { @@ -82,45 +82,39 @@ enum class FilterType { /** * @brief Morphological structuring element shapes */ -enum class StructuringElement { - RECTANGLE, - ELLIPSE, - CROSS, - DIAMOND, - CUSTOM -}; +enum class StructuringElement { RECTANGLE, ELLIPSE, CROSS, DIAMOND, CUSTOM }; /** * @brief Filter parameters container */ struct FilterParams { // Common parameters - double sigma = 1.0; // Standard deviation for Gaussian filters - int kernelSize = 3; // Kernel size (must be odd) - double strength = 1.0; // Filter strength/intensity + double sigma = 1.0; // Standard deviation for Gaussian filters + int kernelSize = 3; // Kernel size (must be odd) + double strength = 1.0; // Filter strength/intensity // Specific parameters - double threshold1 = 100.0; // Lower threshold (Canny) - double threshold2 = 200.0; // Upper threshold (Canny) - double angle = 0.0; // Motion blur angle - int distance = 5; // Motion blur distance + double threshold1 = 100.0; // Lower threshold (Canny) + double threshold2 = 200.0; // Upper threshold (Canny) + double angle = 0.0; // Motion blur angle + int distance = 5; // Motion blur distance // Bilateral filter - double sigmaColor = 75.0; // Color sigma - double sigmaSpace = 75.0; // Space sigma + double sigmaColor = 75.0; // Color sigma + double sigmaSpace = 75.0; // Space sigma // Non-local means - double h = 10.0; // Filter strength - int templateWindowSize = 7; // Template patch size - int searchWindowSize = 21; // Search window size + double h = 10.0; // Filter strength + int templateWindowSize = 7; // Template patch size + int searchWindowSize = 21; // Search window size // Morphological operations StructuringElement structElement = StructuringElement::RECTANGLE; std::vector> customKernel; // Frequency domain - double cutoffFreq = 0.5; // Cutoff frequency (0-1) - double bandwidth = 0.1; // Bandwidth for band filters + double cutoffFreq = 0.5; // Cutoff frequency (0-1) + double bandwidth = 0.1; // Bandwidth for band filters // Custom parameters std::unordered_map custom; @@ -142,7 +136,7 @@ class ImageFilter { * @return Filtered image blob */ virtual blob applyFilter(const blob& input, FilterType filterType, - const FilterParams& params = {}) const; + const FilterParams& params = {}) const; /** * @brief Apply a custom convolution kernel @@ -151,9 +145,9 @@ class ImageFilter { * @param normalize Whether to normalize the kernel * @return Filtered image blob */ - virtual blob applyCustomKernel(const blob& input, - const std::vector>& kernel, - bool normalize = true) const; + virtual blob applyCustomKernel( + const blob& input, const std::vector>& kernel, + bool normalize = true) const; /** * @brief Apply separable filter (more efficient for separable kernels) @@ -163,8 +157,8 @@ class ImageFilter { * @return Filtered image blob */ virtual blob applySeparableFilter(const blob& input, - const std::vector& kernelX, - const std::vector& kernelY) const; + const std::vector& kernelX, + const std::vector& kernelY) const; /** * @brief Apply morphological operation @@ -175,8 +169,8 @@ class ImageFilter { * @return Processed image blob */ virtual blob applyMorphological(const blob& input, FilterType operation, - StructuringElement structElement, - int size = 3) const; + StructuringElement structElement, + int size = 3) const; /** * @brief Apply frequency domain filter @@ -186,7 +180,7 @@ class ImageFilter { * @return Filtered image blob */ virtual blob applyFrequencyFilter(const blob& input, FilterType filterType, - const FilterParams& params = {}) const; + const FilterParams& params = {}) const; /** * @brief Apply adaptive filter based on local image statistics @@ -197,8 +191,8 @@ class ImageFilter { * @return Filtered image blob */ virtual blob applyAdaptiveFilter(const blob& input, FilterType filterType, - int windowSize = 7, - const FilterParams& params = {}) const; + int windowSize = 7, + const FilterParams& params = {}) const; /** * @brief Apply multi-scale filter (pyramid processing) @@ -209,8 +203,8 @@ class ImageFilter { * @return Filtered image blob */ virtual blob applyMultiScaleFilter(const blob& input, FilterType filterType, - int scales = 3, - const FilterParams& params = {}) const; + int scales = 3, + const FilterParams& params = {}) const; /** * @brief Combine multiple filters in sequence @@ -219,9 +213,9 @@ class ImageFilter { * @param params Vector of parameters for each filter * @return Filtered image blob */ - virtual blob applyFilterChain(const blob& input, - const std::vector& filters, - const std::vector& params = {}) const; + virtual blob applyFilterChain( + const blob& input, const std::vector& filters, + const std::vector& params = {}) const; /** * @brief Get predefined kernel for common filters @@ -229,7 +223,8 @@ class ImageFilter { * @param size Kernel size * @return Convolution kernel */ - static std::vector> getPredefinedKernel(FilterType filterType, int size = 3); + static std::vector> getPredefinedKernel( + FilterType filterType, int size = 3); /** * @brief Create Gaussian kernel @@ -237,7 +232,8 @@ class ImageFilter { * @param sigma Standard deviation * @return Gaussian kernel */ - static std::vector> createGaussianKernel(int size, double sigma); + static std::vector> createGaussianKernel(int size, + double sigma); /** * @brief Create motion blur kernel @@ -246,7 +242,8 @@ class ImageFilter { * @param distance Motion distance * @return Motion blur kernel */ - static std::vector> createMotionBlurKernel(int size, double angle, int distance); + static std::vector> createMotionBlurKernel( + int size, double angle, int distance); /** * @brief Create structuring element for morphological operations @@ -254,7 +251,8 @@ class ImageFilter { * @param size Element size * @return Structuring element as binary mask */ - static std::vector> createStructuringElement(StructuringElement shape, int size); + static std::vector> createStructuringElement( + StructuringElement shape, int size); protected: /** @@ -266,9 +264,10 @@ class ImageFilter { * @param channels Number of channels * @return Convolved image data */ - virtual std::vector convolve(const std::vector& input, - const std::vector>& kernel, - int width, int height, int channels) const; + virtual std::vector convolve( + const std::vector& input, + const std::vector>& kernel, int width, int height, + int channels) const; /** * @brief Apply median filter @@ -279,9 +278,9 @@ class ImageFilter { * @param channels Number of channels * @return Filtered image data */ - virtual std::vector medianFilter(const std::vector& input, - int kernelSize, - int width, int height, int channels) const; + virtual std::vector medianFilter( + const std::vector& input, int kernelSize, int width, + int height, int channels) const; /** * @brief Apply bilateral filter @@ -292,9 +291,9 @@ class ImageFilter { * @param channels Number of channels * @return Filtered image data */ - virtual std::vector bilateralFilter(const std::vector& input, - const FilterParams& params, - int width, int height, int channels) const; + virtual std::vector bilateralFilter( + const std::vector& input, const FilterParams& params, + int width, int height, int channels) const; }; /** @@ -304,6 +303,6 @@ class ImageFilter { */ std::unique_ptr createOptimalFilter(bool useGPU = false); -} // namespace atom::image +} // namespace atom::image -#endif // ATOM_IMAGE_FILTERS_HPP +#endif // ATOM_IMAGE_FILTERS_HPP diff --git a/atom/image/processing/gpu_acceleration.cpp b/atom/image/processing/gpu_acceleration.cpp index ae898277..4da29b61 100644 --- a/atom/image/processing/gpu_acceleration.cpp +++ b/atom/image/processing/gpu_acceleration.cpp @@ -5,12 +5,12 @@ #define THROW_RUNTIME_ERROR(msg) throw std::runtime_error(msg) #define THROW_INVALID_ARGUMENT(msg) throw std::invalid_argument(msg) #include -#include #include +#include #ifdef ATOM_IMAGE_HAS_CUDA -#include #include +#include #include #endif @@ -19,8 +19,8 @@ #endif #ifdef ATOM_IMAGE_HAS_OPENCV -#include #include +#include #endif namespace atom::image { @@ -54,26 +54,23 @@ class FallbackGPUBuffer : public GPUBuffer { return true; } - bool copyFrom(const GPUBuffer& src, size_t srcOffset, size_t dstOffset, size_t size) override { + bool copyFrom(const GPUBuffer& src, size_t srcOffset, size_t dstOffset, + size_t size) override { const auto& srcBuffer = static_cast(src); - if (srcOffset + size > srcBuffer.data_.size() || dstOffset + size > data_.size()) { + if (srcOffset + size > srcBuffer.data_.size() || + dstOffset + size > data_.size()) { return false; } - std::memcpy(data_.data() + dstOffset, srcBuffer.data_.data() + srcOffset, size); + std::memcpy(data_.data() + dstOffset, + srcBuffer.data_.data() + srcOffset, size); return true; } - size_t getSize() const override { - return size_; - } + size_t getSize() const override { return size_; } - GPUMemoryType getMemoryType() const override { - return memoryType_; - } + GPUMemoryType getMemoryType() const override { return memoryType_; } - bool isValid() const override { - return !data_.empty(); - } + bool isValid() const override { return !data_.empty(); } void release() override { data_.clear(); @@ -92,14 +89,17 @@ class FallbackGPUKernel : public GPUKernel { FallbackGPUKernel() = default; ~FallbackGPUKernel() override = default; - bool loadFromSource(const std::string& source, const std::string& entryPoint, const std::string& buildOptions) override { + bool loadFromSource(const std::string& source, + const std::string& entryPoint, + const std::string& buildOptions) override { source_ = source; entryPoint_ = entryPoint; buildOptions_ = buildOptions; return true; } - bool loadFromBinary(const std::vector& binary, const std::string& entryPoint) override { + bool loadFromBinary(const std::vector& binary, + const std::string& entryPoint) override { binary_ = binary; entryPoint_ = entryPoint; return true; @@ -115,17 +115,16 @@ class FallbackGPUKernel : public GPUKernel { return true; } - bool execute(const std::vector& globalWorkSize, const std::vector& localWorkSize) override { + bool execute(const std::vector& globalWorkSize, + const std::vector& localWorkSize) override { // Fallback CPU execution would go here return true; } std::unordered_map getInfo() const override { - return { - {"type", "fallback"}, - {"entry_point", entryPoint_}, - {"status", "loaded"} - }; + return {{"type", "fallback"}, + {"entry_point", entryPoint_}, + {"status", "loaded"}}; } private: @@ -148,7 +147,8 @@ class FallbackGPUContext : public GPUContext { return true; } - std::unique_ptr createBuffer(size_t size, GPUMemoryType memoryType) override { + std::unique_ptr createBuffer(size_t size, + GPUMemoryType memoryType) override { auto buffer = std::make_unique(); if (buffer->allocate(size, memoryType)) { return std::move(buffer); @@ -170,7 +170,7 @@ class FallbackGPUContext : public GPUContext { info.name = "Fallback CPU Device"; info.vendor = "Atom Framework"; info.version = "1.0"; - info.totalMemory = 1024 * 1024 * 1024; // 1GB placeholder + info.totalMemory = 1024 * 1024 * 1024; // 1GB placeholder info.computeUnits = 1; info.maxWorkGroupSize = 256; info.supportsDouble = true; @@ -232,7 +232,7 @@ std::unique_ptr GPUImageProcessor::uploadImage(const blob& image) { try { size_t imageSize = image.size(); auto buffer = context_->createBuffer(imageSize, GPUMemoryType::DEVICE); - + if (buffer && buffer->upload(image.data(), imageSize)) { return buffer; } @@ -243,7 +243,8 @@ std::unique_ptr GPUImageProcessor::uploadImage(const blob& image) { return nullptr; } -blob GPUImageProcessor::downloadImage(const GPUBuffer& buffer, int width, int height, int channels) { +blob GPUImageProcessor::downloadImage(const GPUBuffer& buffer, int width, + int height, int channels) { if (!context_ || !buffer.isValid()) { return blob{}; } @@ -251,7 +252,7 @@ blob GPUImageProcessor::downloadImage(const GPUBuffer& buffer, int width, int he try { size_t imageSize = width * height * channels; std::vector data(imageSize); - + if (const_cast(buffer).download(data.data(), imageSize)) { return blob(data.data(), data.size()); } @@ -262,9 +263,9 @@ blob GPUImageProcessor::downloadImage(const GPUBuffer& buffer, int width, int he return blob{}; } -std::unique_ptr GPUImageProcessor::convolve(const GPUBuffer& input, - const std::vector>& kernel, - int width, int height, int channels) { +std::unique_ptr GPUImageProcessor::convolve( + const GPUBuffer& input, const std::vector>& kernel, + int width, int height, int channels) { if (!context_ || !input.isValid()) { return nullptr; } @@ -273,7 +274,7 @@ std::unique_ptr GPUImageProcessor::convolve(const GPUBuffer& input, // Create output buffer size_t outputSize = width * height * channels; auto output = context_->createBuffer(outputSize, GPUMemoryType::DEVICE); - + if (!output) { return nullptr; } @@ -283,21 +284,19 @@ std::unique_ptr GPUImageProcessor::convolve(const GPUBuffer& input, // 2. Set kernel arguments (input, output, kernel weights, dimensions) // 3. Execute kernel with appropriate work group sizes // 4. Return output buffer - + // For fallback, just copy input to output output->copyFrom(input, 0, 0, outputSize); - + return output; } catch (const std::exception&) { return nullptr; } } -std::unique_ptr GPUImageProcessor::resize(const GPUBuffer& input, - int srcWidth, int srcHeight, - int dstWidth, int dstHeight, - int channels, - const std::string& interpolation) { +std::unique_ptr GPUImageProcessor::resize( + const GPUBuffer& input, int srcWidth, int srcHeight, int dstWidth, + int dstHeight, int channels, const std::string& interpolation) { if (!context_ || !input.isValid()) { return nullptr; } @@ -310,8 +309,9 @@ std::unique_ptr GPUImageProcessor::resize(const GPUBuffer& input, return nullptr; } - // In a real implementation, this would use GPU kernels for bilinear/bicubic interpolation - // For fallback, just copy input (no actual resizing) + // In a real implementation, this would use GPU kernels for + // bilinear/bicubic interpolation For fallback, just copy input (no + // actual resizing) size_t copySize = std::min(input.getSize(), outputSize); output->copyFrom(input, 0, 0, copySize); @@ -321,24 +321,24 @@ std::unique_ptr GPUImageProcessor::resize(const GPUBuffer& input, } } -std::unique_ptr GPUImageProcessor::convertColorSpace(const GPUBuffer& input, - const std::string& fromSpace, - const std::string& toSpace, - int width, int height) { +std::unique_ptr GPUImageProcessor::convertColorSpace( + const GPUBuffer& input, const std::string& fromSpace, + const std::string& toSpace, int width, int height) { if (!context_ || !input.isValid() || fromSpace == toSpace) { return nullptr; } try { - size_t outputSize = width * height * 3; // Assume 3 channels for most color spaces + size_t outputSize = + width * height * 3; // Assume 3 channels for most color spaces auto output = context_->createBuffer(outputSize, GPUMemoryType::DEVICE); if (!output) { return nullptr; } - // In a real implementation, this would use GPU kernels for color space conversion - // For fallback, just copy input + // In a real implementation, this would use GPU kernels for color space + // conversion For fallback, just copy input size_t copySize = std::min(input.getSize(), outputSize); output->copyFrom(input, 0, 0, copySize); @@ -348,8 +348,8 @@ std::unique_ptr GPUImageProcessor::convertColorSpace(const GPUBuffer& } } -std::unique_ptr GPUImageProcessor::equalizeHistogram(const GPUBuffer& input, - int width, int height, int channels) { +std::unique_ptr GPUImageProcessor::equalizeHistogram( + const GPUBuffer& input, int width, int height, int channels) { if (!context_ || !input.isValid()) { return nullptr; } @@ -376,10 +376,10 @@ std::unique_ptr GPUImageProcessor::equalizeHistogram(const GPUBuffer& } } -std::unique_ptr GPUImageProcessor::morphological(const GPUBuffer& input, - const std::string& operation, - const std::vector>& structElement, - int width, int height, int channels) { +std::unique_ptr GPUImageProcessor::morphological( + const GPUBuffer& input, const std::string& operation, + const std::vector>& structElement, int width, int height, + int channels) { if (!context_ || !input.isValid()) { return nullptr; } @@ -392,8 +392,8 @@ std::unique_ptr GPUImageProcessor::morphological(const GPUBuffer& inp return nullptr; } - // In a real implementation, this would apply morphological operations (erosion, dilation, etc.) - // For fallback, just copy input + // In a real implementation, this would apply morphological operations + // (erosion, dilation, etc.) For fallback, just copy input output->copyFrom(input, 0, 0, outputSize); return output; @@ -402,24 +402,26 @@ std::unique_ptr GPUImageProcessor::morphological(const GPUBuffer& inp } } -std::unique_ptr GPUImageProcessor::detectEdges(const GPUBuffer& input, - const std::string& method, - float threshold1, float threshold2, - int width, int height) { +std::unique_ptr GPUImageProcessor::detectEdges( + const GPUBuffer& input, const std::string& method, float threshold1, + float threshold2, int width, int height) { if (!context_ || !input.isValid()) { return nullptr; } try { - size_t outputSize = width * height; // Edge detection typically produces grayscale output + size_t outputSize = + width * + height; // Edge detection typically produces grayscale output auto output = context_->createBuffer(outputSize, GPUMemoryType::DEVICE); if (!output) { return nullptr; } - // In a real implementation, this would apply edge detection algorithms (Sobel, Canny, etc.) - // For fallback, just copy input (truncated to single channel) + // In a real implementation, this would apply edge detection algorithms + // (Sobel, Canny, etc.) For fallback, just copy input (truncated to + // single channel) size_t copySize = std::min(input.getSize(), outputSize); output->copyFrom(input, 0, 0, copySize); @@ -429,12 +431,10 @@ std::unique_ptr GPUImageProcessor::detectEdges(const GPUBuffer& input } } -std::unique_ptr GPUImageProcessor::applyCustomKernel(const GPUBuffer& input, - const std::string& kernelSource, - const std::string& entryPoint, - const std::vector& globalWorkSize, - const std::vector& localWorkSize, - const std::vector& args) { +std::unique_ptr GPUImageProcessor::applyCustomKernel( + const GPUBuffer& input, const std::string& kernelSource, + const std::string& entryPoint, const std::vector& globalWorkSize, + const std::vector& localWorkSize, const std::vector& args) { if (!context_ || !input.isValid()) { return nullptr; } @@ -447,7 +447,8 @@ std::unique_ptr GPUImageProcessor::applyCustomKernel(const GPUBuffer& } // Create output buffer (assume same size as input) - auto output = context_->createBuffer(input.getSize(), GPUMemoryType::DEVICE); + auto output = + context_->createBuffer(input.getSize(), GPUMemoryType::DEVICE); if (!output) { return nullptr; } @@ -458,7 +459,8 @@ std::unique_ptr GPUImageProcessor::applyCustomKernel(const GPUBuffer& // Set additional arguments for (size_t i = 0; i < args.size(); ++i) { - kernel->setArgument(static_cast(i + 2), &args[i], sizeof(float)); + kernel->setArgument(static_cast(i + 2), &args[i], + sizeof(float)); } // Execute kernel @@ -476,7 +478,6 @@ std::vector> GPUImageProcessor::batchProcess( const std::vector>& inputs, const std::string& operation, const std::unordered_map& params) { - std::vector> outputs; outputs.reserve(inputs.size()); @@ -491,15 +492,25 @@ std::vector> GPUImageProcessor::batchProcess( if (operation == "gaussian_blur") { float sigma = params.count("sigma") ? params.at("sigma") : 1.0f; - int kernelSize = params.count("kernel_size") ? static_cast(params.at("kernel_size")) : 5; - int width = params.count("width") ? static_cast(params.at("width")) : 512; - int height = params.count("height") ? static_cast(params.at("height")) : 512; - int channels = params.count("channels") ? static_cast(params.at("channels")) : 3; - - result = gaussianBlur(*input, sigma, kernelSize, width, height, channels); + int kernelSize = params.count("kernel_size") + ? static_cast(params.at("kernel_size")) + : 5; + int width = params.count("width") + ? static_cast(params.at("width")) + : 512; + int height = params.count("height") + ? static_cast(params.at("height")) + : 512; + int channels = params.count("channels") + ? static_cast(params.at("channels")) + : 3; + + result = gaussianBlur(*input, sigma, kernelSize, width, height, + channels); } else { // For unknown operations, just copy input - result = context_->createBuffer(input->getSize(), GPUMemoryType::DEVICE); + result = + context_->createBuffer(input->getSize(), GPUMemoryType::DEVICE); if (result) { result->copyFrom(*input, 0, 0, input->getSize()); } @@ -511,18 +522,19 @@ std::vector> GPUImageProcessor::batchProcess( return outputs; } -GPUContext* GPUImageProcessor::getContext() const { - return context_.get(); -} +GPUContext* GPUImageProcessor::getContext() const { return context_.get(); } -std::unordered_map GPUImageProcessor::getPerformanceStats() const { +std::unordered_map GPUImageProcessor::getPerformanceStats() + const { std::unordered_map stats; if (context_) { auto deviceInfo = context_->getDeviceInfo(); - stats["memory_size_mb"] = static_cast(deviceInfo.totalMemory) / (1024 * 1024); + stats["memory_size_mb"] = + static_cast(deviceInfo.totalMemory) / (1024 * 1024); stats["compute_units"] = static_cast(deviceInfo.computeUnits); - stats["max_work_group_size"] = static_cast(deviceInfo.maxWorkGroupSize); + stats["max_work_group_size"] = + static_cast(deviceInfo.maxWorkGroupSize); } // Placeholder performance metrics @@ -533,9 +545,9 @@ std::unordered_map GPUImageProcessor::getPerformanceStats() return stats; } -std::unordered_map GPUImageProcessor::benchmark(const std::string& operation, - const std::pair& imageSize, - int iterations) { +std::unordered_map GPUImageProcessor::benchmark( + const std::string& operation, const std::pair& imageSize, + int iterations) { std::unordered_map results; if (!context_ || iterations <= 0) { @@ -550,7 +562,8 @@ std::unordered_map GPUImageProcessor::benchmark(const std:: size_t imageDataSize = width * height * channels; // Create test input buffer - auto inputBuffer = context_->createBuffer(imageDataSize, GPUMemoryType::DEVICE); + auto inputBuffer = + context_->createBuffer(imageDataSize, GPUMemoryType::DEVICE); if (!inputBuffer) { results["error"] = 1.0; return results; @@ -567,15 +580,20 @@ std::unordered_map GPUImageProcessor::benchmark(const std:: std::unique_ptr output; if (operation == "gaussian_blur") { - output = gaussianBlur(*inputBuffer, 1.0f, 5, width, height, channels); + output = gaussianBlur(*inputBuffer, 1.0f, 5, width, height, + channels); } else if (operation == "resize") { - output = resize(*inputBuffer, width, height, width/2, height/2, channels); + output = resize(*inputBuffer, width, height, width / 2, + height / 2, channels); } else if (operation == "histogram_equalization") { - output = equalizeHistogram(*inputBuffer, width, height, channels); + output = + equalizeHistogram(*inputBuffer, width, height, channels); } else { // Default to convolution - std::vector> kernel = {{0, -1, 0}, {-1, 5, -1}, {0, -1, 0}}; - output = convolve(*inputBuffer, kernel, width, height, channels); + std::vector> kernel = { + {0, -1, 0}, {-1, 5, -1}, {0, -1, 0}}; + output = + convolve(*inputBuffer, kernel, width, height, channels); } // Synchronize to ensure operation completes @@ -583,15 +601,19 @@ std::unordered_map GPUImageProcessor::benchmark(const std:: } auto endTime = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration_cast(endTime - startTime); + auto duration = std::chrono::duration_cast( + endTime - startTime); - double avgTimeMs = static_cast(duration.count()) / (iterations * 1000.0); - double throughputMpixels = (width * height * iterations) / (duration.count() / 1000000.0) / 1000000.0; + double avgTimeMs = + static_cast(duration.count()) / (iterations * 1000.0); + double throughputMpixels = (width * height * iterations) / + (duration.count() / 1000000.0) / 1000000.0; results["avg_time_ms"] = avgTimeMs; results["throughput_mpixels_per_sec"] = throughputMpixels; results["iterations"] = static_cast(iterations); - results["image_size_mpixels"] = static_cast(width * height) / 1000000.0; + results["image_size_mpixels"] = + static_cast(width * height) / 1000000.0; } catch (const std::exception&) { results["error"] = 1.0; @@ -606,15 +628,16 @@ bool GPUImageProcessor::loadBuiltinKernels() { } try { - // In a real implementation, this would load optimized GPU kernels for common operations - // For now, just return success + // In a real implementation, this would load optimized GPU kernels for + // common operations For now, just return success return true; } catch (const std::exception&) { return false; } } -std::string GPUImageProcessor::getKernelSource(const std::string& operation) const { +std::string GPUImageProcessor::getKernelSource( + const std::string& operation) const { // Return placeholder kernel sources // In a real implementation, these would be optimized GPU kernels @@ -689,13 +712,11 @@ std::string GPUImageProcessor::getKernelSource(const std::string& operation) con )"; } - return ""; // Unknown operation + return ""; // Unknown operation } std::unordered_map GPUImageProcessor::optimizeKernelParams( - const std::string& operation, - const std::pair& imageSize) const { - + const std::string& operation, const std::pair& imageSize) const { std::unordered_map params; if (!context_) { @@ -708,8 +729,9 @@ std::unordered_map GPUImageProcessor::optimizeKernelParams( size_t maxWorkGroupSize = deviceInfo.maxWorkGroupSize; // For 2D image processing, use square work groups when possible - size_t workGroupSize = 16; // Common choice for image processing - while (workGroupSize * workGroupSize > maxWorkGroupSize && workGroupSize > 1) { + size_t workGroupSize = 16; // Common choice for image processing + while (workGroupSize * workGroupSize > maxWorkGroupSize && + workGroupSize > 1) { workGroupSize /= 2; } @@ -717,8 +739,10 @@ std::unordered_map GPUImageProcessor::optimizeKernelParams( params["local_work_size_y"] = workGroupSize; // Calculate global work sizes (must be multiples of local work sizes) - size_t globalX = ((imageSize.first + workGroupSize - 1) / workGroupSize) * workGroupSize; - size_t globalY = ((imageSize.second + workGroupSize - 1) / workGroupSize) * workGroupSize; + size_t globalX = + ((imageSize.first + workGroupSize - 1) / workGroupSize) * workGroupSize; + size_t globalY = ((imageSize.second + workGroupSize - 1) / workGroupSize) * + workGroupSize; params["global_work_size_x"] = globalX; params["global_work_size_y"] = globalY; @@ -726,17 +750,17 @@ std::unordered_map GPUImageProcessor::optimizeKernelParams( return params; } - -std::unique_ptr GPUImageProcessor::gaussianBlur(const GPUBuffer& input, - float sigma, int kernelSize, - int width, int height, int channels) { +std::unique_ptr GPUImageProcessor::gaussianBlur( + const GPUBuffer& input, float sigma, int kernelSize, int width, int height, + int channels) { if (!context_ || !input.isValid()) { return nullptr; } try { // Generate Gaussian kernel - std::vector> kernel(kernelSize, std::vector(kernelSize)); + std::vector> kernel(kernelSize, + std::vector(kernelSize)); float sum = 0.0f; int center = kernelSize / 2; @@ -744,7 +768,7 @@ std::unique_ptr GPUImageProcessor::gaussianBlur(const GPUBuffer& inpu for (int j = 0; j < kernelSize; ++j) { float x = i - center; float y = j - center; - kernel[i][j] = std::exp(-(x*x + y*y) / (2 * sigma * sigma)); + kernel[i][j] = std::exp(-(x * x + y * y) / (2 * sigma * sigma)); sum += kernel[i][j]; } } diff --git a/atom/image/processing/gpu_acceleration.hpp b/atom/image/processing/gpu_acceleration.hpp index 3dfca357..0c11f92a 100644 --- a/atom/image/processing/gpu_acceleration.hpp +++ b/atom/image/processing/gpu_acceleration.hpp @@ -14,11 +14,11 @@ * @version 1.0.0 */ -#include "../core/image_blob.hpp" -#include -#include #include +#include #include +#include +#include "../core/image_blob.hpp" namespace atom::image { @@ -40,29 +40,30 @@ enum class GPUBackend { * @brief GPU memory types */ enum class GPUMemoryType { - DEVICE, // Device memory (GPU VRAM) - HOST, // Host memory (CPU RAM) - UNIFIED, // Unified memory (accessible by both CPU and GPU) - PINNED // Pinned host memory (faster transfers) + DEVICE, // Device memory (GPU VRAM) + HOST, // Host memory (CPU RAM) + UNIFIED, // Unified memory (accessible by both CPU and GPU) + PINNED // Pinned host memory (faster transfers) }; /** * @brief GPU device information */ struct GPUDeviceInfo { - int deviceId; // Device ID - std::string name; // Device name - std::string vendor; // Vendor name - GPUBackend backend; // Backend type - size_t totalMemory; // Total memory in bytes - size_t freeMemory; // Free memory in bytes - int computeUnits; // Number of compute units - int maxWorkGroupSize; // Maximum work group size - std::vector maxWorkItemSizes; // Maximum work item sizes - bool supportsDouble; // Supports double precision - bool supportsHalf; // Supports half precision - std::string version; // Driver/runtime version - std::unordered_map extensions; // Supported extensions + int deviceId; // Device ID + std::string name; // Device name + std::string vendor; // Vendor name + GPUBackend backend; // Backend type + size_t totalMemory; // Total memory in bytes + size_t freeMemory; // Free memory in bytes + int computeUnits; // Number of compute units + int maxWorkGroupSize; // Maximum work group size + std::vector maxWorkItemSizes; // Maximum work item sizes + bool supportsDouble; // Supports double precision + bool supportsHalf; // Supports half precision + std::string version; // Driver/runtime version + std::unordered_map + extensions; // Supported extensions }; /** @@ -79,7 +80,8 @@ class GPUBuffer { * @param memoryType Memory type * @return Success status */ - virtual bool allocate(size_t size, GPUMemoryType memoryType = GPUMemoryType::DEVICE) = 0; + virtual bool allocate(size_t size, + GPUMemoryType memoryType = GPUMemoryType::DEVICE) = 0; /** * @brief Upload data to GPU buffer @@ -107,7 +109,8 @@ class GPUBuffer { * @param size Data size * @return Success status */ - virtual bool copyFrom(const GPUBuffer& src, size_t srcOffset, size_t dstOffset, size_t size) = 0; + virtual bool copyFrom(const GPUBuffer& src, size_t srcOffset, + size_t dstOffset, size_t size) = 0; /** * @brief Get buffer size @@ -149,8 +152,8 @@ class GPUKernel { * @return Success status */ virtual bool loadFromSource(const std::string& source, - const std::string& entryPoint, - const std::string& buildOptions = "") = 0; + const std::string& entryPoint, + const std::string& buildOptions = "") = 0; /** * @brief Load kernel from binary @@ -159,7 +162,7 @@ class GPUKernel { * @return Success status */ virtual bool loadFromBinary(const std::vector& binary, - const std::string& entryPoint) = 0; + const std::string& entryPoint) = 0; /** * @brief Set kernel argument @@ -185,7 +188,7 @@ class GPUKernel { * @return Success status */ virtual bool execute(const std::vector& globalWorkSize, - const std::vector& localWorkSize = {}) = 0; + const std::vector& localWorkSize = {}) = 0; /** * @brief Get kernel info @@ -208,7 +211,8 @@ class GPUContext { * @param deviceId Device ID (-1 for auto-select) * @return Success status */ - virtual bool initialize(GPUBackend backend = GPUBackend::AUTO, int deviceId = -1) = 0; + virtual bool initialize(GPUBackend backend = GPUBackend::AUTO, + int deviceId = -1) = 0; /** * @brief Create GPU buffer @@ -216,8 +220,8 @@ class GPUContext { * @param memoryType Memory type * @return GPU buffer pointer */ - virtual std::unique_ptr createBuffer(size_t size, - GPUMemoryType memoryType = GPUMemoryType::DEVICE) = 0; + virtual std::unique_ptr createBuffer( + size_t size, GPUMemoryType memoryType = GPUMemoryType::DEVICE) = 0; /** * @brief Create GPU kernel @@ -242,7 +246,8 @@ class GPUContext { * @param backend GPU backend * @return Vector of device information */ - static std::vector getAvailableDevices(GPUBackend backend = GPUBackend::AUTO); + static std::vector getAvailableDevices( + GPUBackend backend = GPUBackend::AUTO); /** * @brief Check if backend is available @@ -272,7 +277,8 @@ class GPUImageProcessor { * @param deviceId Device ID * @return Success status */ - virtual bool initialize(GPUBackend backend = GPUBackend::AUTO, int deviceId = -1); + virtual bool initialize(GPUBackend backend = GPUBackend::AUTO, + int deviceId = -1); /** * @brief Upload image to GPU @@ -289,7 +295,8 @@ class GPUImageProcessor { * @param channels Number of channels * @return Downloaded image blob */ - virtual blob downloadImage(const GPUBuffer& buffer, int width, int height, int channels); + virtual blob downloadImage(const GPUBuffer& buffer, int width, int height, + int channels); /** * @brief Apply convolution filter on GPU @@ -300,9 +307,9 @@ class GPUImageProcessor { * @param channels Number of channels * @return Filtered image buffer */ - virtual std::unique_ptr convolve(const GPUBuffer& input, - const std::vector>& kernel, - int width, int height, int channels); + virtual std::unique_ptr convolve( + const GPUBuffer& input, const std::vector>& kernel, + int width, int height, int channels); /** * @brief Apply Gaussian blur on GPU @@ -315,8 +322,9 @@ class GPUImageProcessor { * @return Blurred image buffer */ virtual std::unique_ptr gaussianBlur(const GPUBuffer& input, - float sigma, int kernelSize, - int width, int height, int channels); + float sigma, int kernelSize, + int width, int height, + int channels); /** * @brief Resize image on GPU @@ -329,11 +337,10 @@ class GPUImageProcessor { * @param interpolation Interpolation method * @return Resized image buffer */ - virtual std::unique_ptr resize(const GPUBuffer& input, - int srcWidth, int srcHeight, - int dstWidth, int dstHeight, - int channels, - const std::string& interpolation = "linear"); + virtual std::unique_ptr resize( + const GPUBuffer& input, int srcWidth, int srcHeight, int dstWidth, + int dstHeight, int channels, + const std::string& interpolation = "linear"); /** * @brief Apply color space conversion on GPU @@ -344,10 +351,9 @@ class GPUImageProcessor { * @param height Image height * @return Converted image buffer */ - virtual std::unique_ptr convertColorSpace(const GPUBuffer& input, - const std::string& fromSpace, - const std::string& toSpace, - int width, int height); + virtual std::unique_ptr convertColorSpace( + const GPUBuffer& input, const std::string& fromSpace, + const std::string& toSpace, int width, int height); /** * @brief Apply histogram equalization on GPU @@ -358,7 +364,8 @@ class GPUImageProcessor { * @return Equalized image buffer */ virtual std::unique_ptr equalizeHistogram(const GPUBuffer& input, - int width, int height, int channels); + int width, int height, + int channels); /** * @brief Apply morphological operation on GPU @@ -370,10 +377,10 @@ class GPUImageProcessor { * @param channels Number of channels * @return Processed image buffer */ - virtual std::unique_ptr morphological(const GPUBuffer& input, - const std::string& operation, - const std::vector>& structElement, - int width, int height, int channels); + virtual std::unique_ptr morphological( + const GPUBuffer& input, const std::string& operation, + const std::vector>& structElement, int width, + int height, int channels); /** * @brief Apply edge detection on GPU @@ -386,9 +393,10 @@ class GPUImageProcessor { * @return Edge image buffer */ virtual std::unique_ptr detectEdges(const GPUBuffer& input, - const std::string& method, - float threshold1, float threshold2, - int width, int height); + const std::string& method, + float threshold1, + float threshold2, int width, + int height); /** * @brief Apply custom kernel on GPU @@ -400,12 +408,12 @@ class GPUImageProcessor { * @param args Additional kernel arguments * @return Processed image buffer */ - virtual std::unique_ptr applyCustomKernel(const GPUBuffer& input, - const std::string& kernelSource, - const std::string& entryPoint, - const std::vector& globalWorkSize, - const std::vector& localWorkSize = {}, - const std::vector& args = {}); + virtual std::unique_ptr applyCustomKernel( + const GPUBuffer& input, const std::string& kernelSource, + const std::string& entryPoint, + const std::vector& globalWorkSize, + const std::vector& localWorkSize = {}, + const std::vector& args = {}); /** * @brief Batch process multiple images on GPU @@ -438,9 +446,9 @@ class GPUImageProcessor { * @param iterations Number of iterations * @return Benchmark results */ - virtual std::unordered_map benchmark(const std::string& operation, - const std::pair& imageSize, - int iterations = 100); + virtual std::unordered_map benchmark( + const std::string& operation, const std::pair& imageSize, + int iterations = 100); protected: /** @@ -478,9 +486,9 @@ class GPUImageProcessor { * @param deviceId Device ID (-1 for auto-select) * @return Unique pointer to GPU processor */ -std::unique_ptr createOptimalGPUProcessor(GPUBackend backend = GPUBackend::AUTO, - int deviceId = -1); +std::unique_ptr createOptimalGPUProcessor( + GPUBackend backend = GPUBackend::AUTO, int deviceId = -1); -} // namespace atom::image +} // namespace atom::image -#endif // ATOM_IMAGE_GPU_ACCELERATION_HPP +#endif // ATOM_IMAGE_GPU_ACCELERATION_HPP diff --git a/atom/image/processing/image_processor.cpp b/atom/image/processing/image_processor.cpp index 3926d7d8..d4cb6b61 100644 --- a/atom/image/processing/image_processor.cpp +++ b/atom/image/processing/image_processor.cpp @@ -7,8 +7,8 @@ #include #ifdef ATOM_IMAGE_HAS_OPENCV -#include #include +#include #endif // Use standard exceptions to avoid atom error system namespace pollution @@ -16,14 +16,13 @@ #undef THROW_RUNTIME_ERROR // Remove the simple definition #define THROW_RUNTIME_ERROR(msg) throw std::runtime_error(msg) - - namespace atom::image { ImageProcessor::ImageProcessor(const ProcessingOptions& options) : m_options(options) {} -blob ImageProcessor::convertFormat(const blob& input, [[maybe_unused]] ImageFormat targetFormat) const { +blob ImageProcessor::convertFormat( + const blob& input, [[maybe_unused]] ImageFormat targetFormat) const { if (input.size() == 0) { THROW_RUNTIME_ERROR("Cannot convert empty image"); } @@ -57,8 +56,11 @@ blob ImageProcessor::convertFormat(const blob& input, [[maybe_unused]] ImageForm #endif } -blob ImageProcessor::resize(const blob& input [[maybe_unused]], int newWidth [[maybe_unused]], int newHeight [[maybe_unused]], - const std::string& algorithm [[maybe_unused]]) const { +blob ImageProcessor::resize(const blob& input [[maybe_unused]], + int newWidth [[maybe_unused]], + int newHeight [[maybe_unused]], + const std::string& algorithm + [[maybe_unused]]) const { validateImageDimensions(newWidth, newHeight); #ifdef ATOM_IMAGE_HAS_OPENCV @@ -74,14 +76,17 @@ blob ImageProcessor::resize(const blob& input [[maybe_unused]], int newWidth [[m interpolation = cv::INTER_LANCZOS4; } - cv::resize(inputMat, outputMat, cv::Size(newWidth, newHeight), 0, 0, interpolation); + cv::resize(inputMat, outputMat, cv::Size(newWidth, newHeight), 0, 0, + interpolation); return blob(outputMat); #else THROW_RUNTIME_ERROR("Resize operation requires OpenCV support"); #endif } -blob ImageProcessor::rotate(const blob& input [[maybe_unused]], double angle [[maybe_unused]], bool expandCanvas [[maybe_unused]]) const { +blob ImageProcessor::rotate(const blob& input [[maybe_unused]], + double angle [[maybe_unused]], + bool expandCanvas [[maybe_unused]]) const { #ifdef ATOM_IMAGE_HAS_OPENCV cv::Mat inputMat = input.to_mat(); cv::Mat outputMat; @@ -91,7 +96,8 @@ blob ImageProcessor::rotate(const blob& input [[maybe_unused]], double angle [[m if (expandCanvas) { // Calculate new image size to fit rotated image - cv::Rect2f bbox = cv::RotatedRect(center, inputMat.size(), angle).boundingRect2f(); + cv::Rect2f bbox = + cv::RotatedRect(center, inputMat.size(), angle).boundingRect2f(); // Adjust transformation matrix rotationMatrix.at(0, 2) += bbox.width / 2.0 - center.x; @@ -108,7 +114,8 @@ blob ImageProcessor::rotate(const blob& input [[maybe_unused]], double angle [[m #endif } -blob ImageProcessor::crop(const blob& input, int x, int y, int width, int height) const { +blob ImageProcessor::crop(const blob& input, int x, int y, int width, + int height) const { validateCropParameters(input, x, y, width, height); #ifdef ATOM_IMAGE_HAS_OPENCV @@ -121,21 +128,26 @@ blob ImageProcessor::crop(const blob& input, int x, int y, int width, int height #endif } -blob ImageProcessor::applyFilter(const blob& input [[maybe_unused]], FilterType filterType [[maybe_unused]], - const std::unordered_map& parameters [[maybe_unused]]) const { +blob ImageProcessor::applyFilter( + const blob& input [[maybe_unused]], FilterType filterType [[maybe_unused]], + const std::unordered_map& parameters + [[maybe_unused]]) const { #ifdef ATOM_IMAGE_HAS_OPENCV switch (filterType) { case FilterType::GAUSSIAN_BLUR: { - double sigma = parameters.count("sigma") ? parameters.at("sigma") : 1.0; + double sigma = + parameters.count("sigma") ? parameters.at("sigma") : 1.0; return applyGaussianBlur(input, sigma); } case FilterType::SHARPEN: { - double strength = parameters.count("strength") ? parameters.at("strength") : 1.0; + double strength = + parameters.count("strength") ? parameters.at("strength") : 1.0; return applySharpen(input, strength); } case FilterType::MEDIAN: { - int kernelSize = parameters.count("kernelSize") ? - static_cast(parameters.at("kernelSize")) : 5; + int kernelSize = parameters.count("kernelSize") + ? static_cast(parameters.at("kernelSize")) + : 5; return applyMedianFilter(input, kernelSize); } case FilterType::EDGE_DETECT: { @@ -150,8 +162,9 @@ blob ImageProcessor::applyFilter(const blob& input [[maybe_unused]], FilterType } blob ImageProcessor::applyCustomKernel(const blob& input [[maybe_unused]], - const std::vector& kernel [[maybe_unused]], - int kernelSize [[maybe_unused]]) const { + const std::vector& kernel + [[maybe_unused]], + int kernelSize [[maybe_unused]]) const { validateKernel(kernel, kernelSize); #ifdef ATOM_IMAGE_HAS_OPENCV @@ -169,25 +182,27 @@ blob ImageProcessor::applyCustomKernel(const blob& input [[maybe_unused]], #endif } -blob ImageProcessor::adjustBrightnessContrast(const blob& input [[maybe_unused]], - double brightness [[maybe_unused]], - double contrast [[maybe_unused]]) const { +blob ImageProcessor::adjustBrightnessContrast( + const blob& input [[maybe_unused]], double brightness [[maybe_unused]], + double contrast [[maybe_unused]]) const { #ifdef ATOM_IMAGE_HAS_OPENCV cv::Mat inputMat = input.to_mat(); cv::Mat outputMat; // Convert brightness and contrast to OpenCV format double alpha = (contrast + 100.0) / 100.0; // Contrast multiplier - double beta = brightness; // Brightness offset + double beta = brightness; // Brightness offset inputMat.convertTo(outputMat, -1, alpha, beta); return blob(outputMat); #else - THROW_RUNTIME_ERROR("Brightness/contrast adjustment requires OpenCV support"); + THROW_RUNTIME_ERROR( + "Brightness/contrast adjustment requires OpenCV support"); #endif } -blob ImageProcessor::adjustGamma(const blob& input [[maybe_unused]], double gamma [[maybe_unused]]) const { +blob ImageProcessor::adjustGamma(const blob& input [[maybe_unused]], + double gamma [[maybe_unused]]) const { if (gamma <= 0.0) { THROW_RUNTIME_ERROR("Gamma value must be positive"); } @@ -200,7 +215,8 @@ blob ImageProcessor::adjustGamma(const blob& input [[maybe_unused]], double gamm cv::Mat lookupTable(1, 256, CV_8U); uchar* p = lookupTable.ptr(); for (int i = 0; i < 256; ++i) { - p[i] = cv::saturate_cast(std::pow(i / 255.0, 1.0 / gamma) * 255.0); + p[i] = + cv::saturate_cast(std::pow(i / 255.0, 1.0 / gamma) * 255.0); } cv::LUT(inputMat, lookupTable, outputMat); @@ -210,7 +226,8 @@ blob ImageProcessor::adjustGamma(const blob& input [[maybe_unused]], double gamm #endif } -blob ImageProcessor::enhanceHistogram(const blob& input [[maybe_unused]], bool adaptive [[maybe_unused]]) const { +blob ImageProcessor::enhanceHistogram(const blob& input [[maybe_unused]], + bool adaptive [[maybe_unused]]) const { #ifdef ATOM_IMAGE_HAS_OPENCV cv::Mat inputMat = input.to_mat(); cv::Mat outputMat; @@ -253,24 +270,23 @@ blob ImageProcessor::enhanceHistogram(const blob& input [[maybe_unused]], bool a std::vector ImageProcessor::processBatch( const std::vector& inputs, std::function operation) const { - std::vector results(inputs.size()); if (m_options.useMultithreading && inputs.size() > 1) { // Parallel processing - std::transform(std::execution::par_unseq, - inputs.begin(), inputs.end(), - results.begin(), operation); + std::transform(std::execution::par_unseq, inputs.begin(), inputs.end(), + results.begin(), operation); } else { // Sequential processing - std::transform(inputs.begin(), inputs.end(), - results.begin(), operation); + std::transform(inputs.begin(), inputs.end(), results.begin(), + operation); } return results; } -std::unordered_map ImageProcessor::getStatistics(const blob& input [[maybe_unused]]) const { +std::unordered_map ImageProcessor::getStatistics( + const blob& input [[maybe_unused]]) const { std::unordered_map stats; #ifdef ATOM_IMAGE_HAS_OPENCV @@ -306,22 +322,26 @@ const ProcessingOptions& ImageProcessor::getOptions() const noexcept { } // Private helper methods -blob ImageProcessor::applyGaussianBlur(const blob& input [[maybe_unused]], double sigma [[maybe_unused]]) const { +blob ImageProcessor::applyGaussianBlur(const blob& input [[maybe_unused]], + double sigma [[maybe_unused]]) const { #ifdef ATOM_IMAGE_HAS_OPENCV cv::Mat inputMat = input.to_mat(); cv::Mat outputMat; int kernelSize = static_cast(2 * std::ceil(3 * sigma) + 1); - if (kernelSize % 2 == 0) kernelSize++; // Ensure odd kernel size + if (kernelSize % 2 == 0) + kernelSize++; // Ensure odd kernel size - cv::GaussianBlur(inputMat, outputMat, cv::Size(kernelSize, kernelSize), sigma); + cv::GaussianBlur(inputMat, outputMat, cv::Size(kernelSize, kernelSize), + sigma); return blob(outputMat); #else THROW_RUNTIME_ERROR("Gaussian blur requires OpenCV support"); #endif } -blob ImageProcessor::applySharpen(const blob& input [[maybe_unused]], double strength [[maybe_unused]]) const { +blob ImageProcessor::applySharpen(const blob& input [[maybe_unused]], + double strength [[maybe_unused]]) const { #ifdef ATOM_IMAGE_HAS_OPENCV cv::Mat inputMat = input.to_mat(); cv::Mat blurred, outputMat; @@ -372,7 +392,8 @@ blob ImageProcessor::convertToTIFF(const blob& input) const { #ifdef ATOM_IMAGE_HAS_OPENCV cv::Mat inputMat = input.to_mat(); std::vector buffer; - std::vector params = {cv::IMWRITE_TIFF_COMPRESSION, 1}; // LZW compression + std::vector params = {cv::IMWRITE_TIFF_COMPRESSION, + 1}; // LZW compression if (!cv::imencode(".tiff", inputMat, buffer, params)) { THROW_RUNTIME_ERROR("Failed to encode image as TIFF"); @@ -394,7 +415,8 @@ void ImageProcessor::validateImageDimensions(int width, int height) const { } } -void ImageProcessor::validateKernel(const std::vector& kernel, int kernelSize) const { +void ImageProcessor::validateKernel(const std::vector& kernel, + int kernelSize) const { if (kernelSize <= 0 || kernelSize % 2 == 0) { THROW_RUNTIME_ERROR("Kernel size must be positive and odd"); } @@ -403,7 +425,8 @@ void ImageProcessor::validateKernel(const std::vector& kernel, int kernel } } -void ImageProcessor::validateCropParameters(const blob& input, int x, int y, int width, int height) const { +void ImageProcessor::validateCropParameters(const blob& input, int x, int y, + int width, int height) const { if (x < 0 || y < 0 || width <= 0 || height <= 0) { THROW_RUNTIME_ERROR("Invalid crop parameters"); } @@ -412,13 +435,15 @@ void ImageProcessor::validateCropParameters(const blob& input, int x, int y, int } } -std::unique_ptr createOptimalProcessor(bool useGPU [[maybe_unused]]) { +std::unique_ptr createOptimalProcessor(bool useGPU + [[maybe_unused]]) { ProcessingOptions options; options.useMultithreading = true; options.enableSIMD = true; - options.maxMemoryUsage = std::thread::hardware_concurrency() * 256 * 1024 * 1024; // 256MB per thread + options.maxMemoryUsage = std::thread::hardware_concurrency() * 256 * 1024 * + 1024; // 256MB per thread return std::make_unique(options); } -} // namespace atom::image +} // namespace atom::image diff --git a/atom/image/processing/image_processor.hpp b/atom/image/processing/image_processor.hpp index 56972e75..8edbdcef 100644 --- a/atom/image/processing/image_processor.hpp +++ b/atom/image/processing/image_processor.hpp @@ -5,8 +5,8 @@ #include #include #include -#include #include +#include #include "../core/image_blob.hpp" #include "../io/format_detector.hpp" @@ -47,7 +47,8 @@ struct ProcessingOptions { /** * @class ImageProcessor - * @brief High-performance image processing pipeline with format conversion and filtering + * @brief High-performance image processing pipeline with format conversion and + * filtering */ class ImageProcessor { public: @@ -55,7 +56,8 @@ class ImageProcessor { * @brief Construct a new ImageProcessor * @param options Processing configuration options */ - explicit ImageProcessor(const ProcessingOptions& options = ProcessingOptions{}); + explicit ImageProcessor( + const ProcessingOptions& options = ProcessingOptions{}); /** * @brief Destructor @@ -76,18 +78,20 @@ class ImageProcessor { * @param targetFormat Target format * @return Converted image blob */ - [[nodiscard]] blob convertFormat(const blob& input, ImageFormat targetFormat) const; + [[nodiscard]] blob convertFormat(const blob& input, + ImageFormat targetFormat) const; /** * @brief Resize image with various algorithms * @param input Input image blob * @param newWidth Target width * @param newHeight Target height - * @param algorithm Resize algorithm ("nearest", "linear", "cubic", "lanczos") + * @param algorithm Resize algorithm ("nearest", "linear", "cubic", + * "lanczos") * @return Resized image blob */ [[nodiscard]] blob resize(const blob& input, int newWidth, int newHeight, - const std::string& algorithm = "cubic") const; + const std::string& algorithm = "cubic") const; /** * @brief Rotate image by specified angle @@ -96,7 +100,8 @@ class ImageProcessor { * @param expandCanvas Whether to expand canvas to fit rotated image * @return Rotated image blob */ - [[nodiscard]] blob rotate(const blob& input, double angle, bool expandCanvas = true) const; + [[nodiscard]] blob rotate(const blob& input, double angle, + bool expandCanvas = true) const; /** * @brief Crop image to specified rectangle @@ -107,7 +112,8 @@ class ImageProcessor { * @param height Height of crop area * @return Cropped image blob */ - [[nodiscard]] blob crop(const blob& input, int x, int y, int width, int height) const; + [[nodiscard]] blob crop(const blob& input, int x, int y, int width, + int height) const; /** * @brief Apply filter to image @@ -116,8 +122,9 @@ class ImageProcessor { * @param parameters Filter-specific parameters * @return Filtered image blob */ - [[nodiscard]] blob applyFilter(const blob& input, FilterType filterType, - const std::unordered_map& parameters = {}) const; + [[nodiscard]] blob applyFilter( + const blob& input, FilterType filterType, + const std::unordered_map& parameters = {}) const; /** * @brief Apply custom convolution kernel @@ -127,8 +134,8 @@ class ImageProcessor { * @return Filtered image blob */ [[nodiscard]] blob applyCustomKernel(const blob& input, - const std::vector& kernel, - int kernelSize) const; + const std::vector& kernel, + int kernelSize) const; /** * @brief Adjust image brightness and contrast @@ -138,8 +145,8 @@ class ImageProcessor { * @return Adjusted image blob */ [[nodiscard]] blob adjustBrightnessContrast(const blob& input, - double brightness, - double contrast) const; + double brightness, + double contrast) const; /** * @brief Apply gamma correction @@ -155,7 +162,8 @@ class ImageProcessor { * @param adaptive Whether to use adaptive histogram equalization * @return Enhanced image blob */ - [[nodiscard]] blob enhanceHistogram(const blob& input, bool adaptive = false) const; + [[nodiscard]] blob enhanceHistogram(const blob& input, + bool adaptive = false) const; /** * @brief Detect edges in image @@ -165,19 +173,21 @@ class ImageProcessor { * @return Edge-detected image blob */ [[nodiscard]] blob detectEdges(const blob& input, - const std::string& algorithm = "canny", - const std::vector& threshold = {50.0, 150.0}) const; + const std::string& algorithm = "canny", + const std::vector& threshold = { + 50.0, 150.0}) const; /** * @brief Remove noise from image * @param input Input image blob - * @param algorithm Denoising algorithm ("gaussian", "median", "bilateral", "nlmeans") + * @param algorithm Denoising algorithm ("gaussian", "median", "bilateral", + * "nlmeans") * @param strength Denoising strength (0.0 to 1.0) * @return Denoised image blob */ [[nodiscard]] blob denoise(const blob& input, - const std::string& algorithm = "bilateral", - double strength = 0.5) const; + const std::string& algorithm = "bilateral", + double strength = 0.5) const; /** * @brief Process batch of images with the same operation @@ -194,7 +204,8 @@ class ImageProcessor { * @param input Input image blob * @return Map of statistics (mean, std, min, max, etc.) */ - [[nodiscard]] std::unordered_map getStatistics(const blob& input) const; + [[nodiscard]] std::unordered_map getStatistics( + const blob& input) const; /** * @brief Calculate image quality metrics @@ -202,9 +213,9 @@ class ImageProcessor { * @param reference Optional reference image for comparison metrics * @return Map of quality metrics */ - [[nodiscard]] std::unordered_map calculateQualityMetrics( - const blob& input, - const blob* reference = nullptr) const; + [[nodiscard]] std::unordered_map + calculateQualityMetrics(const blob& input, + const blob* reference = nullptr) const; /** * @brief Set processing options @@ -224,7 +235,8 @@ class ImageProcessor { // Internal helper methods [[nodiscard]] blob applyGaussianBlur(const blob& input, double sigma) const; [[nodiscard]] blob applySharpen(const blob& input, double strength) const; - [[nodiscard]] blob applyMedianFilter(const blob& input, int kernelSize) const; + [[nodiscard]] blob applyMedianFilter(const blob& input, + int kernelSize) const; // Format-specific converters [[nodiscard]] blob convertToJPEG(const blob& input) const; @@ -234,7 +246,8 @@ class ImageProcessor { // Validation helpers void validateImageDimensions(int width, int height) const; void validateKernel(const std::vector& kernel, int kernelSize) const; - void validateCropParameters(const blob& input, int x, int y, int width, int height) const; + void validateCropParameters(const blob& input, int x, int y, int width, + int height) const; }; /** @@ -242,8 +255,9 @@ class ImageProcessor { * @param useGPU Whether to enable GPU acceleration if available * @return Configured ImageProcessor instance */ -[[nodiscard]] std::unique_ptr createOptimalProcessor(bool useGPU = false); +[[nodiscard]] std::unique_ptr createOptimalProcessor( + bool useGPU = false); -} // namespace atom::image +} // namespace atom::image -#endif // ATOM_IMAGE_PROCESSOR_HPP +#endif // ATOM_IMAGE_PROCESSOR_HPP diff --git a/atom/image/processing/ml_processing.cpp b/atom/image/processing/ml_processing.cpp index 41acc7dd..a31aea9d 100644 --- a/atom/image/processing/ml_processing.cpp +++ b/atom/image/processing/ml_processing.cpp @@ -1,6 +1,6 @@ #include "ml_processing.hpp" -#include #include +#include #include // Define error macros to avoid atom error system namespace pollution @@ -8,24 +8,23 @@ #define THROW_INVALID_ARGUMENT(msg) throw std::invalid_argument(msg) #include #include +#include #include #include -#include #ifdef ATOM_IMAGE_HAS_ONNX #include #endif #ifdef ATOM_IMAGE_HAS_OPENCV -#include #include +#include #endif namespace atom::image { bool MLImageProcessor::initialize(const std::string& modelDir, - MLBackend backend, - bool useGPU) { + MLBackend backend, bool useGPU) { modelDir_ = modelDir; currentBackend_ = backend; useGPU_ = useGPU; @@ -36,84 +35,92 @@ bool MLImageProcessor::initialize(const std::string& modelDir, // 2. Set up GPU/CPU execution providers // 3. Load common models from modelDir // 4. Validate model compatibility - + return true; } catch (const std::exception&) { return false; } } -MLResult MLImageProcessor::superResolution(const blob& input, - MLModelType model, - const MLParams& params) const { +MLResult MLImageProcessor::superResolution(const blob& input, MLModelType model, + const MLParams& params) const { MLResult result; result.success = false; - + if (input.isEmpty()) { result.errorMessage = "Input image is empty"; return result; } - + try { // Use model to determine processing - (void)model; // if not used further - + (void)model; // if not used further + auto preprocessed = preprocessImage(input, model, params); if (preprocessed.empty()) { result.errorMessage = "Preprocessing failed"; return result; } - + auto inferenceOutput = runInference(preprocessed, model, params); if (inferenceOutput.empty()) { result.errorMessage = "Inference failed"; return result; } - - auto outputBlob = postprocessOutput(inferenceOutput, model, {static_cast(input.getWidth()), static_cast(input.getHeight())}, params); + + auto outputBlob = + postprocessOutput(inferenceOutput, model, + {static_cast(input.getWidth()), + static_cast(input.getHeight())}, + params); if (outputBlob.isEmpty()) { result.errorMessage = "Postprocessing failed"; return result; } - + result.outputImage = outputBlob; result.success = true; - result.modelUsed = "fallback_" + std::to_string(static_cast(model)); - result.processingTime = 0.1; // placeholder + result.modelUsed = + "fallback_" + std::to_string(static_cast(model)); + result.processingTime = 0.1; // placeholder } catch (const std::exception& e) { - result.errorMessage = std::string("Super-resolution failed: ") + e.what(); + result.errorMessage = + std::string("Super-resolution failed: ") + e.what(); } - + return result; } -MLResult MLImageProcessor::denoise(const blob& input, - MLModelType model, - const MLParams& params) const { +MLResult MLImageProcessor::denoise(const blob& input, MLModelType model, + const MLParams& params) const { MLResult result; result.success = false; - + if (input.isEmpty()) { result.errorMessage = "Input image is empty"; return result; } - + try { (void)model; - + auto preprocessed = preprocessImage(input, model, params); if (preprocessed.empty()) { result.errorMessage = "Preprocessing failed"; return result; } - + auto inferenceOutput = runInference(preprocessed, model, params); if (inferenceOutput.empty()) { result.errorMessage = "Inference failed"; return result; } - - auto outputBlob = postprocessOutput(inferenceOutput, model, {static_cast(input.getWidth()), static_cast(input.getHeight())}, params); + + auto outputBlob = + postprocessOutput(inferenceOutput, model, + {static_cast(input.getWidth()), + static_cast(input.getHeight())}, + params); result.outputImage = outputBlob; result.success = !outputBlob.isEmpty(); result.modelUsed = "fallback_denoise"; @@ -121,34 +128,38 @@ MLResult MLImageProcessor::denoise(const blob& input, } catch (const std::exception& e) { result.errorMessage = std::string("Denoising failed: ") + e.what(); } - + return result; } MLResult MLImageProcessor::styleTransfer(const blob& contentImage, - const blob& styleImage, - MLModelType model, - const MLParams& params) const { + const blob& styleImage, + MLModelType model, + const MLParams& params) const { MLResult result; result.success = false; - + if (contentImage.isEmpty() || styleImage.isEmpty()) { result.errorMessage = "Content or style image is empty"; return result; } - + try { (void)model; - (void)styleImage; // For now, use content as base - + (void)styleImage; // For now, use content as base + auto preprocessed = preprocessImage(contentImage, model, params); if (preprocessed.empty()) { result.errorMessage = "Preprocessing failed"; return result; } - + auto inferenceOutput = runInference(preprocessed, model, params); - auto outputBlob = postprocessOutput(inferenceOutput, model, {static_cast(contentImage.getWidth()), static_cast(contentImage.getHeight())}, params); + auto outputBlob = + postprocessOutput(inferenceOutput, model, + {static_cast(contentImage.getWidth()), + static_cast(contentImage.getHeight())}, + params); result.outputImage = outputBlob; result.success = !outputBlob.isEmpty(); result.modelUsed = "fallback_style"; @@ -156,42 +167,45 @@ MLResult MLImageProcessor::styleTransfer(const blob& contentImage, } catch (const std::exception& e) { result.errorMessage = std::string("Style transfer failed: ") + e.what(); } - + return result; } -MLResult MLImageProcessor::enhance(const blob& input, - MLModelType model, - const MLParams& params) const { +MLResult MLImageProcessor::enhance(const blob& input, MLModelType model, + const MLParams& params) const { MLResult result; result.success = false; - + if (input.isEmpty()) { result.errorMessage = "Input image is empty"; return result; } - + try { (void)model; - + auto preprocessed = preprocessImage(input, model, params); if (preprocessed.empty()) { result.errorMessage = "Preprocessing failed"; return result; } - + auto inferenceOutput = runInference(preprocessed, model, params); if (inferenceOutput.empty()) { result.errorMessage = "Inference failed"; return result; } - - auto outputBlob = postprocessOutput(inferenceOutput, model, {static_cast(input.getWidth()), static_cast(input.getHeight())}, params); + + auto outputBlob = + postprocessOutput(inferenceOutput, model, + {static_cast(input.getWidth()), + static_cast(input.getHeight())}, + params); if (outputBlob.isEmpty()) { result.errorMessage = "Postprocessing failed"; return result; } - + result.outputImage = outputBlob; result.success = true; result.modelUsed = "fallback_enhance"; @@ -199,42 +213,45 @@ MLResult MLImageProcessor::enhance(const blob& input, } catch (const std::exception& e) { result.errorMessage = std::string("Enhancement failed: ") + e.what(); } - + return result; } -MLResult MLImageProcessor::restore(const blob& input, - MLModelType model, - const MLParams& params) const { +MLResult MLImageProcessor::restore(const blob& input, MLModelType model, + const MLParams& params) const { MLResult result; result.success = false; - + if (input.isEmpty()) { result.errorMessage = "Input image is empty"; return result; } - + try { (void)model; - + auto preprocessed = preprocessImage(input, model, params); if (preprocessed.empty()) { result.errorMessage = "Preprocessing failed"; return result; } - + auto inferenceOutput = runInference(preprocessed, model, params); if (inferenceOutput.empty()) { result.errorMessage = "Inference failed"; return result; } - - auto outputBlob = postprocessOutput(inferenceOutput, model, {static_cast(input.getWidth()), static_cast(input.getHeight())}, params); + + auto outputBlob = + postprocessOutput(inferenceOutput, model, + {static_cast(input.getWidth()), + static_cast(input.getHeight())}, + params); if (outputBlob.isEmpty()) { result.errorMessage = "Postprocessing failed"; return result; } - + result.outputImage = outputBlob; result.success = true; result.modelUsed = "fallback_restore"; @@ -242,16 +259,18 @@ MLResult MLImageProcessor::restore(const blob& input, } catch (const std::exception& e) { result.errorMessage = std::string("Restoration failed: ") + e.what(); } - + return result; } MLResult MLImageProcessor::generateFromText(const std::string& prompt, - MLModelType model, - const MLParams& params) const { + MLModelType model, + const MLParams& params) const { MLResult result; result.success = false; - result.errorMessage = "Text-to-image generation for prompt '" + prompt + "' not implemented in fallback; requires diffusion models like Stable Diffusion"; + result.errorMessage = "Text-to-image generation for prompt '" + prompt + + "' not implemented in fallback; requires diffusion " + "models like Stable Diffusion"; (void)model; (void)params; // Could use params.prompt, but already has @@ -261,10 +280,9 @@ MLResult MLImageProcessor::generateFromText(const std::string& prompt, return result; } -MLResult MLImageProcessor::inpaint(const blob& input, - const blob& mask, - MLModelType model, - const MLParams& params) const { +MLResult MLImageProcessor::inpaint(const blob& input, const blob& mask, + MLModelType model, + const MLParams& params) const { MLResult result; result.success = false; @@ -276,25 +294,29 @@ MLResult MLImageProcessor::inpaint(const blob& input, try { (void)mask; (void)model; - + auto preprocessed = preprocessImage(input, model, params); if (preprocessed.empty()) { result.errorMessage = "Preprocessing failed"; return result; } - + auto inferenceOutput = runInference(preprocessed, model, params); if (inferenceOutput.empty()) { result.errorMessage = "Inference failed"; return result; } - - auto outputBlob = postprocessOutput(inferenceOutput, model, {static_cast(input.getWidth()), static_cast(input.getHeight())}, params); + + auto outputBlob = + postprocessOutput(inferenceOutput, model, + {static_cast(input.getWidth()), + static_cast(input.getHeight())}, + params); if (outputBlob.isEmpty()) { result.errorMessage = "Postprocessing failed"; return result; } - + result.outputImage = outputBlob; result.success = true; result.modelUsed = "fallback_inpaint"; @@ -306,9 +328,8 @@ MLResult MLImageProcessor::inpaint(const blob& input, return result; } -MLResult MLImageProcessor::colorize(const blob& input, - MLModelType model, - const MLParams& params) const { +MLResult MLImageProcessor::colorize(const blob& input, MLModelType model, + const MLParams& params) const { MLResult result; result.success = false; @@ -319,25 +340,29 @@ MLResult MLImageProcessor::colorize(const blob& input, try { (void)model; - + auto preprocessed = preprocessImage(input, model, params); if (preprocessed.empty()) { result.errorMessage = "Preprocessing failed"; return result; } - + auto inferenceOutput = runInference(preprocessed, model, params); if (inferenceOutput.empty()) { result.errorMessage = "Inference failed"; return result; } - - auto outputBlob = postprocessOutput(inferenceOutput, model, {static_cast(input.getWidth()), static_cast(input.getHeight())}, params); + + auto outputBlob = + postprocessOutput(inferenceOutput, model, + {static_cast(input.getWidth()), + static_cast(input.getHeight())}, + params); if (outputBlob.isEmpty()) { result.errorMessage = "Postprocessing failed"; return result; } - + result.outputImage = outputBlob; result.success = true; result.modelUsed = "fallback_colorize"; @@ -350,8 +375,8 @@ MLResult MLImageProcessor::colorize(const blob& input, } MLResult MLImageProcessor::removeBackground(const blob& input, - MLModelType model, - const MLParams& params) const { + MLModelType model, + const MLParams& params) const { MLResult result; result.success = false; @@ -362,39 +387,43 @@ MLResult MLImageProcessor::removeBackground(const blob& input, try { (void)model; - + auto preprocessed = preprocessImage(input, model, params); if (preprocessed.empty()) { result.errorMessage = "Preprocessing failed"; return result; } - + auto inferenceOutput = runInference(preprocessed, model, params); if (inferenceOutput.empty()) { result.errorMessage = "Inference failed"; return result; } - - auto outputBlob = postprocessOutput(inferenceOutput, model, {static_cast(input.getWidth()), static_cast(input.getHeight())}, params); + + auto outputBlob = + postprocessOutput(inferenceOutput, model, + {static_cast(input.getWidth()), + static_cast(input.getHeight())}, + params); if (outputBlob.isEmpty()) { result.errorMessage = "Postprocessing failed"; return result; } - + result.outputImage = outputBlob; result.success = true; result.modelUsed = "fallback_remove_bg"; result.processingTime = 0.1; } catch (const std::exception& e) { - result.errorMessage = std::string("Background removal failed: ") + e.what(); + result.errorMessage = + std::string("Background removal failed: ") + e.what(); } return result; } -MLResult MLImageProcessor::restoreFaces(const blob& input, - MLModelType model, - const MLParams& params) const { +MLResult MLImageProcessor::restoreFaces(const blob& input, MLModelType model, + const MLParams& params) const { MLResult result; result.success = false; @@ -409,18 +438,20 @@ MLResult MLImageProcessor::restoreFaces(const blob& input, result = enhance(input, MLModelType::MIRNET, params); if (result.success) { result.modelUsed = "fallback_face_restore"; - result.processingTime = 0.5; // Face restoration is typically slower + result.processingTime = + 0.5; // Face restoration is typically slower } } catch (const std::exception& e) { - result.errorMessage = std::string("Face restoration failed: ") + e.what(); + result.errorMessage = + std::string("Face restoration failed: ") + e.what(); } return result; } -MLResult MLImageProcessor::processWithCustomModel(const blob& input, - const std::string& modelPath, - const MLParams& params) const { +MLResult MLImageProcessor::processWithCustomModel( + const blob& input, const std::string& modelPath, + const MLParams& params) const { MLResult result; result.success = false; @@ -436,42 +467,45 @@ MLResult MLImageProcessor::processWithCustomModel(const blob& input, try { (void)modelPath; - + auto preprocessed = preprocessImage(input, MLModelType::CUSTOM, params); if (preprocessed.empty()) { result.errorMessage = "Preprocessing failed"; return result; } - - auto inferenceOutput = runInference(preprocessed, MLModelType::CUSTOM, params); + + auto inferenceOutput = + runInference(preprocessed, MLModelType::CUSTOM, params); if (inferenceOutput.empty()) { result.errorMessage = "Inference failed"; return result; } - - auto outputBlob = postprocessOutput(inferenceOutput, MLModelType::CUSTOM, {static_cast(input.getWidth()), static_cast(input.getHeight())}, params); + + auto outputBlob = + postprocessOutput(inferenceOutput, MLModelType::CUSTOM, + {static_cast(input.getWidth()), + static_cast(input.getHeight())}, + params); if (outputBlob.isEmpty()) { result.errorMessage = "Postprocessing failed"; return result; } - + result.outputImage = outputBlob; result.success = true; result.modelUsed = "custom_" + modelPath; result.processingTime = 0.2; } catch (const std::exception& e) { - result.errorMessage = std::string("Custom model processing failed: ") + e.what(); + result.errorMessage = + std::string("Custom model processing failed: ") + e.what(); } return result; } std::vector MLImageProcessor::batchProcess( - const std::vector& inputs, - MLModelType model, - const MLParams& params, + const std::vector& inputs, MLModelType model, const MLParams& params, std::function progressCallback) const { - std::vector results; results.reserve(inputs.size()); @@ -495,8 +529,8 @@ std::vector MLImageProcessor::batchProcess( result = denoise(inputs[i], model, params); break; case MLModelType::MIRNET: - // case MLModelType::ZERO_DCE: - // case MLModelType::ENLIGHTENGAN: + // case MLModelType::ZERO_DCE: + // case MLModelType::ENLIGHTENGAN: result = enhance(inputs[i], model, params); break; case MLModelType::SWINIR: @@ -522,14 +556,16 @@ std::vector MLImageProcessor::batchProcess( // Call progress callback if provided if (progressCallback) { - progressCallback(static_cast(i + 1), static_cast(inputs.size())); + progressCallback(static_cast(i + 1), + static_cast(inputs.size())); } } return results; } -std::vector MLImageProcessor::getAvailableModels(MLModelType modelType) const { +std::vector MLImageProcessor::getAvailableModels( + MLModelType modelType) const { std::vector models; // Return placeholder model names based on type @@ -540,7 +576,12 @@ std::vector MLImageProcessor::getAvailableModels(MLModelType modelT case MLModelType::VDSR: case MLModelType::EDSR: case MLModelType::WAIFU2X: - models = {"Real-ESRGAN-x4plus", "ESRGAN-x4", "SRCNN-x2", "VDSR-x4", "EDSR-x4", "waifu2x-cunet"}; + models = {"Real-ESRGAN-x4plus", + "ESRGAN-x4", + "SRCNN-x2", + "VDSR-x4", + "EDSR-x4", + "waifu2x-cunet"}; break; case MLModelType::DNCNN: case MLModelType::FFDNet: @@ -556,9 +597,9 @@ std::vector MLImageProcessor::getAvailableModels(MLModelType modelT return models; } -bool MLImageProcessor::downloadModel(MLModelType model, - const std::string& modelDir, - std::function progressCallback) const { +bool MLImageProcessor::downloadModel( + MLModelType model, const std::string& modelDir, + std::function progressCallback) const { (void)model; if (!modelDir.empty()) { // Would create directory modelDir if needed @@ -569,15 +610,16 @@ bool MLImageProcessor::downloadModel(MLModelType model, std::this_thread::sleep_for(std::chrono::milliseconds(100)); progressCallback(100); } - return false; // Placeholder, actual would download + return false; // Placeholder, actual would download } bool MLImageProcessor::isModelAvailable(MLModelType model) const { (void)model; - return false; // Placeholder + return false; // Placeholder } -std::unordered_map MLImageProcessor::getModelInfo(MLModelType model) const { +std::unordered_map MLImageProcessor::getModelInfo( + MLModelType model) const { std::unordered_map info; // Placeholder model information @@ -604,10 +646,7 @@ std::unordered_map MLImageProcessor::getModelInfo(MLMo } std::unordered_map MLImageProcessor::benchmarkModel( - MLModelType model, - const blob& testImage, - int iterations) const { - + MLModelType model, const blob& testImage, int iterations) const { (void)model; std::unordered_map results; @@ -621,10 +660,12 @@ std::unordered_map MLImageProcessor::benchmarkModel( auto start = std::chrono::high_resolution_clock::now(); for (int i = 0; i < iterations; ++i) { auto res = superResolution(testImage, model, {}); - if (!res.success) break; + if (!res.success) + break; } auto end = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration_cast(end - start); + auto duration = + std::chrono::duration_cast(end - start); results["avg_time_ms"] = static_cast(duration.count()) / iterations; results["iterations"] = iterations; return results; @@ -636,110 +677,121 @@ bool MLImageProcessor::setBackend(MLBackend backend, int deviceId) { return true; } -bool MLImageProcessor::loadModel(MLModelType model, const MLParams& params) const { +bool MLImageProcessor::loadModel(MLModelType model, + const MLParams& params) const { (void)model; (void)params; return false; } -std::vector MLImageProcessor::preprocessImage(const blob& input, - MLModelType model, - const MLParams& params) const { +std::vector MLImageProcessor::preprocessImage( + const blob& input, MLModelType model, const MLParams& params) const { std::vector result; - if (input.isEmpty()) return result; - - (void)model; // Can use to set specific preprocessing - + if (input.isEmpty()) + return result; + + (void)model; // Can use to set specific preprocessing + int targetSize = params.tileSize > 0 ? params.tileSize : 256; - double noiseLevel = params.noiseLevel; // Use for denoising specific + double noiseLevel = params.noiseLevel; // Use for denoising specific // other params used - + #ifdef ATOM_IMAGE_HAS_OPENCV cv::Mat mat = input.to_mat(); cv::Mat processed = mat.clone(); - + // Basic preprocessing: resize, normalize - double scale = std::min(static_cast(targetSize) / processed.cols, static_cast(targetSize) / processed.rows); - cv::Size newSize(static_cast(processed.cols * scale), static_cast(processed.rows * scale)); + double scale = std::min(static_cast(targetSize) / processed.cols, + static_cast(targetSize) / processed.rows); + cv::Size newSize(static_cast(processed.cols * scale), + static_cast(processed.rows * scale)); cv::resize(processed, processed, newSize); - + // For denoising, apply light blur if noiseLevel high if (noiseLevel > 0) { - double sigma = noiseLevel / 255.0 * 10.0; // Scale to reasonable sigma - int kernelSize = static_cast(sigma * 6 / 2 * 2 + 1); // Odd kernel - kernelSize = std::max(3, std::min(kernelSize, 31)); // Clamp - cv::GaussianBlur(processed, processed, cv::Size(kernelSize, kernelSize), sigma); + double sigma = noiseLevel / 255.0 * 10.0; // Scale to reasonable sigma + int kernelSize = static_cast(sigma * 6 / 2 * 2 + 1); // Odd kernel + kernelSize = std::max(3, std::min(kernelSize, 31)); // Clamp + cv::GaussianBlur(processed, processed, cv::Size(kernelSize, kernelSize), + sigma); } - + // Normalize to 0-1 float - processed.convertTo(processed, CV_32FC(static_cast(processed.channels())), 1.0 / 255.0); - + processed.convertTo(processed, + CV_32FC(static_cast(processed.channels())), + 1.0 / 255.0); + if (processed.isContinuous()) { - result.assign((float*)processed.data, (float*)processed.data + processed.total() * processed.channels()); + result.assign( + (float*)processed.data, + (float*)processed.data + processed.total() * processed.channels()); } #endif - + return result; } -blob MLImageProcessor::postprocessOutput(const std::vector& output, - MLModelType model, - const std::pair& originalSize, - const MLParams& params) const { +blob MLImageProcessor::postprocessOutput( + const std::vector& output, MLModelType model, + const std::pair& originalSize, const MLParams& params) const { blob result; - if (output.empty()) return result; - + if (output.empty()) + return result; + (void)model; (void)params; int width = originalSize.first; int height = originalSize.second; - + (void)width; (void)height; - + #ifdef ATOM_IMAGE_HAS_OPENCV // Assume output is float 0-1, channels last or first, assume HWC - int channels = 3; // assume RGB - int h = static_cast(std::sqrt(static_cast(output.size()) / channels)); - int w = h; // assume square for simplicity - + int channels = 3; // assume RGB + int h = static_cast( + std::sqrt(static_cast(output.size()) / channels)); + int w = h; // assume square for simplicity + if (h * w * channels != static_cast(output.size())) { - return result; // invalid + return result; // invalid } - + cv::Mat mat(h, w, CV_32FC(channels), const_cast(output.data())); - + // Denormalize to 0-255 uint8 cv::Mat uint8Mat; mat.convertTo(uint8Mat, CV_8UC(channels), 255.0); - + // Resize to original cv::Mat resized; cv::resize(uint8Mat, resized, cv::Size(width, height)); - + result = blob(resized); #endif - + return result; } -std::vector MLImageProcessor::runInference(const std::vector& input, - MLModelType model, - const MLParams& params) const { - std::vector result = input; // Placeholder: return input as is for fallback +std::vector MLImageProcessor::runInference( + const std::vector& input, MLModelType model, + const MLParams& params) const { + std::vector result = + input; // Placeholder: return input as is for fallback (void)model; (void)params; // In real, would run model inference - // For example, for super resolution, apply simple interpolation in frequency or something, but placeholder copy + // For example, for super resolution, apply simple interpolation in + // frequency or something, but placeholder copy return result; } -// Apply similar pattern to other public methods like enhance, restore, etc., calling the protected methods +// Apply similar pattern to other public methods like enhance, restore, etc., +// calling the protected methods } // namespace atom::image -std::unique_ptr createOptimalMLProcessor(const std::string& modelDir, - bool useGPU, - atom::image::MLBackend backend) { +std::unique_ptr createOptimalMLProcessor( + const std::string& modelDir, bool useGPU, atom::image::MLBackend backend) { auto processor = std::make_unique(); processor->initialize(modelDir, backend, useGPU); return processor; diff --git a/atom/image/processing/ml_processing.hpp b/atom/image/processing/ml_processing.hpp index fe686b51..0c99849a 100644 --- a/atom/image/processing/ml_processing.hpp +++ b/atom/image/processing/ml_processing.hpp @@ -14,12 +14,12 @@ * @version 1.0.0 */ -#include "../core/image_blob.hpp" -#include -#include +#include #include +#include #include -#include +#include +#include "../core/image_blob.hpp" namespace atom::image { @@ -28,66 +28,66 @@ namespace atom::image { */ enum class MLModelType { // Super-resolution models - ESRGAN, // Enhanced Super-Resolution GAN - REAL_ESRGAN, // Real-ESRGAN - SRCNN, // Super-Resolution CNN - VDSR, // Very Deep Super-Resolution - EDSR, // Enhanced Deep Super-Resolution - WAIFU2X, // Waifu2x anime upscaler + ESRGAN, // Enhanced Super-Resolution GAN + REAL_ESRGAN, // Real-ESRGAN + SRCNN, // Super-Resolution CNN + VDSR, // Very Deep Super-Resolution + EDSR, // Enhanced Deep Super-Resolution + WAIFU2X, // Waifu2x anime upscaler // Denoising models - DNCNN, // Denoising CNN - FFDNet, // Fast and Flexible Denoising - RIDNET, // Real Image Denoising - CBDNet, // Toward Convolutional Blind Denoising + DNCNN, // Denoising CNN + FFDNet, // Fast and Flexible Denoising + RIDNET, // Real Image Denoising + CBDNet, // Toward Convolutional Blind Denoising // Style transfer models - NEURAL_STYLE, // Neural Style Transfer - FAST_STYLE, // Fast Style Transfer - ADAIN, // Adaptive Instance Normalization - PHOTOREALISTIC, // Photorealistic Style Transfer + NEURAL_STYLE, // Neural Style Transfer + FAST_STYLE, // Fast Style Transfer + ADAIN, // Adaptive Instance Normalization + PHOTOREALISTIC, // Photorealistic Style Transfer // Image enhancement models - DPED, // DSLR-Quality Photos Enhancement - WESPE, // Weakly Supervised Photo Enhancer - MIRNET, // Learning Enriched Features - RETINEX_NET, // Deep Retinex Decomposition + DPED, // DSLR-Quality Photos Enhancement + WESPE, // Weakly Supervised Photo Enhancer + MIRNET, // Learning Enriched Features + RETINEX_NET, // Deep Retinex Decomposition // Image restoration models - NAFNET, // Nonlinear Activation Free Network - RESTORMER, // Efficient Transformer for Image Restoration - SWINIR, // SwinIR Image Restoration - UFORMER, // U-shaped Transformer + NAFNET, // Nonlinear Activation Free Network + RESTORMER, // Efficient Transformer for Image Restoration + SWINIR, // SwinIR Image Restoration + UFORMER, // U-shaped Transformer // Generative models - STABLE_DIFFUSION, // Stable Diffusion - DALLE, // DALL-E - MIDJOURNEY, // Midjourney-style generation - GAN_PAINT, // GAN-based inpainting + STABLE_DIFFUSION, // Stable Diffusion + DALLE, // DALL-E + MIDJOURNEY, // Midjourney-style generation + GAN_PAINT, // GAN-based inpainting // Specialized models - COLORIZATION, // Image colorization - INPAINTING, // Image inpainting - OUTPAINTING, // Image outpainting - BACKGROUND_REMOVAL, // Background removal - FACE_RESTORATION, // Face restoration + COLORIZATION, // Image colorization + INPAINTING, // Image inpainting + OUTPAINTING, // Image outpainting + BACKGROUND_REMOVAL, // Background removal + FACE_RESTORATION, // Face restoration - CUSTOM // Custom trained model + CUSTOM // Custom trained model }; /** * @brief ML inference backends */ enum class MLBackend { - ONNX, // ONNX Runtime - TENSORRT, // NVIDIA TensorRT - OPENVINO, // Intel OpenVINO - PYTORCH, // PyTorch - TENSORFLOW, // TensorFlow - NCNN, // NCNN (mobile) - MNN, // MNN (mobile) - PADDLE, // PaddlePaddle - AUTO // Auto-select best backend + ONNX, // ONNX Runtime + TENSORRT, // NVIDIA TensorRT + OPENVINO, // Intel OpenVINO + PYTORCH, // PyTorch + TENSORFLOW, // TensorFlow + NCNN, // NCNN (mobile) + MNN, // MNN (mobile) + PADDLE, // PaddlePaddle + AUTO // Auto-select best backend }; /** @@ -95,39 +95,39 @@ enum class MLBackend { */ struct MLParams { // Model parameters - std::string modelPath; // Path to model file - MLBackend backend = MLBackend::AUTO; // Inference backend - bool useGPU = true; // Use GPU acceleration - int gpuDeviceId = 0; // GPU device ID + std::string modelPath; // Path to model file + MLBackend backend = MLBackend::AUTO; // Inference backend + bool useGPU = true; // Use GPU acceleration + int gpuDeviceId = 0; // GPU device ID // Processing parameters - int batchSize = 1; // Batch size for processing - int tileSize = 512; // Tile size for large images - int overlap = 32; // Tile overlap - bool enableTTA = false; // Test-time augmentation + int batchSize = 1; // Batch size for processing + int tileSize = 512; // Tile size for large images + int overlap = 32; // Tile overlap + bool enableTTA = false; // Test-time augmentation // Super-resolution parameters - int scaleFactor = 4; // Upscaling factor - bool preserveDetails = true; // Preserve fine details + int scaleFactor = 4; // Upscaling factor + bool preserveDetails = true; // Preserve fine details // Denoising parameters - double noiseLevel = 25.0; // Noise level (0-100) - bool blindDenoising = true; // Blind denoising mode + double noiseLevel = 25.0; // Noise level (0-100) + bool blindDenoising = true; // Blind denoising mode // Style transfer parameters - double styleStrength = 1.0; // Style transfer strength - bool preserveColor = false; // Preserve original colors + double styleStrength = 1.0; // Style transfer strength + bool preserveColor = false; // Preserve original colors // Enhancement parameters - double enhancementStrength = 0.8; // Enhancement strength - bool autoAdjust = true; // Auto-adjust parameters + double enhancementStrength = 0.8; // Enhancement strength + bool autoAdjust = true; // Auto-adjust parameters // Generation parameters - std::string prompt; // Text prompt for generation - std::string negativePrompt; // Negative prompt - int steps = 50; // Inference steps - double guidanceScale = 7.5; // Guidance scale - int seed = -1; // Random seed (-1 = random) + std::string prompt; // Text prompt for generation + std::string negativePrompt; // Negative prompt + int steps = 50; // Inference steps + double guidanceScale = 7.5; // Guidance scale + int seed = -1; // Random seed (-1 = random) // Custom parameters std::unordered_map customParams; @@ -137,13 +137,13 @@ struct MLParams { * @brief ML processing result */ struct MLResult { - blob outputImage; // Processed image - double processingTime = 0.0; // Processing time in seconds - double confidence = 0.0; // Result confidence - std::string modelUsed; // Model that was used - std::unordered_map metrics; // Quality metrics - std::string errorMessage; // Error message if failed - bool success = true; // Success status + blob outputImage; // Processed image + double processingTime = 0.0; // Processing time in seconds + double confidence = 0.0; // Result confidence + std::string modelUsed; // Model that was used + std::unordered_map metrics; // Quality metrics + std::string errorMessage; // Error message if failed + bool success = true; // Success status }; /** @@ -162,8 +162,8 @@ class MLImageProcessor { * @return Success status */ virtual bool initialize(const std::string& modelDir = "", - MLBackend backend = MLBackend::AUTO, - bool useGPU = true); + MLBackend backend = MLBackend::AUTO, + bool useGPU = true); /** * @brief Apply super-resolution to image @@ -172,9 +172,9 @@ class MLImageProcessor { * @param params Processing parameters * @return Super-resolved image result */ - virtual MLResult superResolution(const blob& input, - MLModelType model = MLModelType::REAL_ESRGAN, - const MLParams& params = {}) const; + virtual MLResult superResolution( + const blob& input, MLModelType model = MLModelType::REAL_ESRGAN, + const MLParams& params = {}) const; /** * @brief Apply AI-based denoising @@ -184,8 +184,8 @@ class MLImageProcessor { * @return Denoised image result */ virtual MLResult denoise(const blob& input, - MLModelType model = MLModelType::DNCNN, - const MLParams& params = {}) const; + MLModelType model = MLModelType::DNCNN, + const MLParams& params = {}) const; /** * @brief Apply neural style transfer @@ -196,9 +196,9 @@ class MLImageProcessor { * @return Stylized image result */ virtual MLResult styleTransfer(const blob& contentImage, - const blob& styleImage, - MLModelType model = MLModelType::FAST_STYLE, - const MLParams& params = {}) const; + const blob& styleImage, + MLModelType model = MLModelType::FAST_STYLE, + const MLParams& params = {}) const; /** * @brief Apply AI-based image enhancement @@ -208,8 +208,8 @@ class MLImageProcessor { * @return Enhanced image result */ virtual MLResult enhance(const blob& input, - MLModelType model = MLModelType::MIRNET, - const MLParams& params = {}) const; + MLModelType model = MLModelType::MIRNET, + const MLParams& params = {}) const; /** * @brief Apply image restoration @@ -219,8 +219,8 @@ class MLImageProcessor { * @return Restored image result */ virtual MLResult restore(const blob& input, - MLModelType model = MLModelType::SWINIR, - const MLParams& params = {}) const; + MLModelType model = MLModelType::SWINIR, + const MLParams& params = {}) const; /** * @brief Generate image from text prompt @@ -229,9 +229,10 @@ class MLImageProcessor { * @param params Generation parameters * @return Generated image result */ - virtual MLResult generateFromText(const std::string& prompt, - MLModelType model = MLModelType::STABLE_DIFFUSION, - const MLParams& params = {}) const; + virtual MLResult generateFromText( + const std::string& prompt, + MLModelType model = MLModelType::STABLE_DIFFUSION, + const MLParams& params = {}) const; /** * @brief Apply image inpainting @@ -241,10 +242,9 @@ class MLImageProcessor { * @param params Processing parameters * @return Inpainted image result */ - virtual MLResult inpaint(const blob& input, - const blob& mask, - MLModelType model = MLModelType::GAN_PAINT, - const MLParams& params = {}) const; + virtual MLResult inpaint(const blob& input, const blob& mask, + MLModelType model = MLModelType::GAN_PAINT, + const MLParams& params = {}) const; /** * @brief Apply image colorization @@ -254,8 +254,8 @@ class MLImageProcessor { * @return Colorized image result */ virtual MLResult colorize(const blob& input, - MLModelType model = MLModelType::COLORIZATION, - const MLParams& params = {}) const; + MLModelType model = MLModelType::COLORIZATION, + const MLParams& params = {}) const; /** * @brief Remove background from image @@ -264,9 +264,9 @@ class MLImageProcessor { * @param params Processing parameters * @return Image with background removed */ - virtual MLResult removeBackground(const blob& input, - MLModelType model = MLModelType::BACKGROUND_REMOVAL, - const MLParams& params = {}) const; + virtual MLResult removeBackground( + const blob& input, MLModelType model = MLModelType::BACKGROUND_REMOVAL, + const MLParams& params = {}) const; /** * @brief Apply face restoration @@ -275,9 +275,9 @@ class MLImageProcessor { * @param params Processing parameters * @return Image with restored faces */ - virtual MLResult restoreFaces(const blob& input, - MLModelType model = MLModelType::FACE_RESTORATION, - const MLParams& params = {}) const; + virtual MLResult restoreFaces( + const blob& input, MLModelType model = MLModelType::FACE_RESTORATION, + const MLParams& params = {}) const; /** * @brief Process image with custom model @@ -287,8 +287,8 @@ class MLImageProcessor { * @return Processed image result */ virtual MLResult processWithCustomModel(const blob& input, - const std::string& modelPath, - const MLParams& params = {}) const; + const std::string& modelPath, + const MLParams& params = {}) const; /** * @brief Batch process multiple images @@ -299,8 +299,7 @@ class MLImageProcessor { * @return Vector of processing results */ virtual std::vector batchProcess( - const std::vector& inputs, - MLModelType model, + const std::vector& inputs, MLModelType model, const MLParams& params = {}, std::function progressCallback = nullptr) const; @@ -309,7 +308,8 @@ class MLImageProcessor { * @param modelType Type of models to list * @return Vector of available model names */ - virtual std::vector getAvailableModels(MLModelType modelType) const; + virtual std::vector getAvailableModels( + MLModelType modelType) const; /** * @brief Download model from repository @@ -318,9 +318,9 @@ class MLImageProcessor { * @param progressCallback Download progress callback * @return Success status */ - virtual bool downloadModel(MLModelType model, - const std::string& modelDir = "", - std::function progressCallback = nullptr) const; + virtual bool downloadModel( + MLModelType model, const std::string& modelDir = "", + std::function progressCallback = nullptr) const; /** * @brief Check if model is available @@ -334,7 +334,8 @@ class MLImageProcessor { * @param model Model to get info for * @return Model information map */ - virtual std::unordered_map getModelInfo(MLModelType model) const; + virtual std::unordered_map getModelInfo( + MLModelType model) const; /** * @brief Benchmark model performance @@ -344,9 +345,7 @@ class MLImageProcessor { * @return Benchmark results */ virtual std::unordered_map benchmarkModel( - MLModelType model, - const blob& testImage, - int iterations = 10) const; + MLModelType model, const blob& testImage, int iterations = 10) const; /** * @brief Set inference backend @@ -369,7 +368,8 @@ class MLImageProcessor { * @param params Model parameters * @return Success status */ - virtual bool loadModel(MLModelType model, const MLParams& params = {}) const; + virtual bool loadModel(MLModelType model, + const MLParams& params = {}) const; /** * @brief Preprocess image for ML model @@ -378,9 +378,9 @@ class MLImageProcessor { * @param params Processing parameters * @return Preprocessed image data */ - virtual std::vector preprocessImage(const blob& input, - MLModelType model, - const MLParams& params = {}) const; + virtual std::vector preprocessImage( + const blob& input, MLModelType model, + const MLParams& params = {}) const; /** * @brief Postprocess ML model output @@ -391,9 +391,9 @@ class MLImageProcessor { * @return Postprocessed image blob */ virtual blob postprocessOutput(const std::vector& output, - MLModelType model, - const std::pair& originalSize, - const MLParams& params = {}) const; + MLModelType model, + const std::pair& originalSize, + const MLParams& params = {}) const; /** * @brief Run inference on preprocessed data @@ -403,8 +403,8 @@ class MLImageProcessor { * @return Model output data */ virtual std::vector runInference(const std::vector& input, - MLModelType model, - const MLParams& params = {}) const; + MLModelType model, + const MLParams& params = {}) const; private: std::string modelDir_; @@ -419,10 +419,10 @@ class MLImageProcessor { * @param backend Preferred inference backend * @return Unique pointer to ML processor */ -std::unique_ptr createOptimalMLProcessor(const std::string& modelDir = "", - bool useGPU = true, - MLBackend backend = MLBackend::AUTO); +std::unique_ptr createOptimalMLProcessor( + const std::string& modelDir = "", bool useGPU = true, + MLBackend backend = MLBackend::AUTO); -} // namespace atom::image +} // namespace atom::image -#endif // ATOM_IMAGE_ML_PROCESSING_HPP +#endif // ATOM_IMAGE_ML_PROCESSING_HPP diff --git a/atom/image/processing/ocr/ocr.cpp b/atom/image/processing/ocr/ocr.cpp index 42f5fadc..37045451 100644 --- a/atom/image/processing/ocr/ocr.cpp +++ b/atom/image/processing/ocr/ocr.cpp @@ -83,10 +83,9 @@ void ProgressReporter::reportProgress() const { if (m_current > 0 && elapsed > 0) { float itemsPerSecond = static_cast(m_current) / elapsed; if (itemsPerSecond > 0) { - int etaSeconds = static_cast((m_total - m_current) / - itemsPerSecond); - eta = std::format("{}m {}s", etaSeconds / 60, - etaSeconds % 60); + int etaSeconds = + static_cast((m_total - m_current) / itemsPerSecond); + eta = std::format("{}m {}s", etaSeconds / 60, etaSeconds % 60); } } @@ -107,7 +106,8 @@ std::string OCRCache::calculateHash(const cv::Mat& img) const { std::vector buffer; cv::imencode(".jpg", img, buffer); - // Using a simple hash function, in production use a stronger hash like SHA-256 + // Using a simple hash function, in production use a stronger hash like + // SHA-256 size_t hash = 0; for (const auto& byte : buffer) { hash = (hash * 31) + byte; @@ -148,7 +148,8 @@ std::optional OCRCache::get(const cv::Mat& img) { std::istreambuf_iterator()); // Update memory cache - if (content.size() < 1024 * 10) { // Only cache small results in memory + if (content.size() < + 1024 * 10) { // Only cache small results in memory m_memoryCache[key] = content; } @@ -195,10 +196,9 @@ void OCRCache::cleanCacheIfNeeded() { // If cache is too large, remove oldest files if (totalSize > m_maxCacheSize) { // Sort by last write time (oldest first) - std::sort(files.begin(), files.end(), - [](const auto& a, const auto& b) { - return a.second < b.second; - }); + std::sort(files.begin(), files.end(), [](const auto& a, const auto& b) { + return a.second < b.second; + }); // Remove oldest files until we're under the limit for (const auto& [path, time] : files) { @@ -247,15 +247,14 @@ void SpellChecker::loadDictionary(const std::string& filePath) { } } -void SpellChecker::addWord(const std::string& word) { - m_dictionary[word]++; -} +void SpellChecker::addWord(const std::string& word) { m_dictionary[word]++; } bool SpellChecker::isCorrect(const std::string& word) { return m_dictionary.count(word) > 0; } -int SpellChecker::levenshteinDistance(const std::string& s1, const std::string& s2) { +int SpellChecker::levenshteinDistance(const std::string& s1, + const std::string& s2) { const std::size_t len1 = s1.size(), len2 = s2.size(); std::vector> d(len1 + 1, std::vector(len2 + 1)); @@ -266,9 +265,9 @@ int SpellChecker::levenshteinDistance(const std::string& s1, const std::string& for (int i = 1; i <= len1; ++i) { for (int j = 1; j <= len2; ++j) { - d[i][j] = std::min( - {d[i - 1][j] + 1, d[i][j - 1] + 1, - d[i - 1][j - 1] + (s1[i - 1] == s2[j - 1] ? 0 : 1)}); + d[i][j] = + std::min({d[i - 1][j] + 1, d[i][j - 1] + 1, + d[i - 1][j - 1] + (s1[i - 1] == s2[j - 1] ? 0 : 1)}); } } @@ -372,11 +371,10 @@ EnhancedOCRProcessor::EnhancedOCRProcessor(const OCRConfig& config) m_tessApi.SetPageSegMode(tesseract::PSM_AUTO); } -EnhancedOCRProcessor::~EnhancedOCRProcessor() { - m_tessApi.End(); -} +EnhancedOCRProcessor::~EnhancedOCRProcessor() { m_tessApi.End(); } -bool EnhancedOCRProcessor::detectLanguage(const cv::Mat& image, std::string& detectedLanguage) { +bool EnhancedOCRProcessor::detectLanguage(const cv::Mat& image, + std::string& detectedLanguage) { // In a real implementation, this would use a language detection model // For simplicity, we'll assume English detectedLanguage = "eng"; @@ -414,7 +412,8 @@ cv::Mat EnhancedOCRProcessor::deskew(const cv::Mat& image) { // Threshold the image cv::Mat binary; - cv::threshold(gray, binary, 0, 255, cv::THRESH_BINARY_INV | cv::THRESH_OTSU); + cv::threshold(gray, binary, 0, 255, + cv::THRESH_BINARY_INV | cv::THRESH_OTSU); // Find all contours std::vector> contours; @@ -455,7 +454,8 @@ cv::Mat EnhancedOCRProcessor::deskew(const cv::Mat& image) { return rotated; } -std::vector EnhancedOCRProcessor::detectTextRegions(const cv::Mat& image) { +std::vector EnhancedOCRProcessor::detectTextRegions( + const cv::Mat& image) { std::vector textBoxes; try { @@ -479,8 +479,8 @@ std::vector EnhancedOCRProcessor::detectTextRegions(const cv::Mat& ima // Set the blob as input and get output layer names m_textDetector.setInput(blob); - std::vector outNames = { - "feature_fusion/Conv_7/Sigmoid", "feature_fusion/concat_3"}; + std::vector outNames = {"feature_fusion/Conv_7/Sigmoid", + "feature_fusion/concat_3"}; // Forward pass std::vector outputBlobs; @@ -525,9 +525,8 @@ std::vector EnhancedOCRProcessor::detectTextRegions(const cv::Mat& ima offsetY - (sin * x1Data[x] - cos * x2Data[x])); cv::Point2f p1 = cv::Point2f(-sin * h, -cos * h) + offset; cv::Point2f p3 = cv::Point2f(-cos * w, sin * w) + offset; - cv::RotatedRect r( - 0.5f * (p1 + p3), cv::Size2f(w, h), - -angle * 180.0f / static_cast(CV_PI)); + cv::RotatedRect r(0.5f * (p1 + p3), cv::Size2f(w, h), + -angle * 180.0f / static_cast(CV_PI)); detections.push_back(r); confidences.push_back(score); @@ -567,13 +566,12 @@ std::vector EnhancedOCRProcessor::detectTextRegions(const cv::Mat& ima // Create axis-aligned bounding box with margins int margin = 10; - cv::Rect rect( - std::max(0, static_cast(minX) - margin), - std::max(0, static_cast(minY) - margin), - std::min(static_cast(width) - 1, - static_cast(maxX - minX) + 2 * margin), - std::min(static_cast(height) - 1, - static_cast(maxY - minY) + 2 * margin)); + cv::Rect rect(std::max(0, static_cast(minX) - margin), + std::max(0, static_cast(minY) - margin), + std::min(static_cast(width) - 1, + static_cast(maxX - minX) + 2 * margin), + std::min(static_cast(height) - 1, + static_cast(maxY - minY) + 2 * margin)); textBoxes.push_back(rect); } @@ -585,4 +583,4 @@ std::vector EnhancedOCRProcessor::detectTextRegions(const cv::Mat& ima return textBoxes; } -#endif // ATOM_IMAGE_HAS_OCR +#endif // ATOM_IMAGE_HAS_OCR diff --git a/atom/image/processing/realtime.cpp b/atom/image/processing/realtime.cpp index 38c53c4a..9a496399 100644 --- a/atom/image/processing/realtime.cpp +++ b/atom/image/processing/realtime.cpp @@ -9,9 +9,9 @@ #include #ifdef ATOM_IMAGE_HAS_OPENCV +#include #include #include -#include #endif namespace atom::image { @@ -23,10 +23,10 @@ bool RealtimeProcessor::initialize(const RealtimeParams& params) { maxBufferSize_ = params.maxBufferSize; enableFrameDropping_ = params.enableFrameDropping; processingMode_ = params.mode; - + // Initialize statistics stats_ = ProcessingStats{}; - + return true; } catch (const std::exception&) { return false; @@ -34,30 +34,30 @@ bool RealtimeProcessor::initialize(const RealtimeParams& params) { } bool RealtimeProcessor::startCapture(CaptureSource source, - const std::string& sourcePath, - FrameCallback frameCallback, - AnalysisCallback analysisCallback) { + const std::string& sourcePath, + FrameCallback frameCallback, + AnalysisCallback analysisCallback) { if (running_.load()) { - return false; // Already running + return false; // Already running } - + // Store callbacks frameCallback_ = frameCallback; analysisCallback_ = analysisCallback; - + // Initialize capture source if (!initializeCapture(source, sourcePath)) { return false; } - + // Start processing running_.store(true); paused_.store(false); - + // Start threads captureThread_ = std::thread(&RealtimeProcessor::captureThread, this); processingThread_ = std::thread(&RealtimeProcessor::processingThread, this); - + return true; } @@ -65,12 +65,12 @@ void RealtimeProcessor::stop() { if (!running_.load()) { return; } - + running_.store(false); - + // Notify threads to wake up frameCondition_.notify_all(); - + // Wait for threads to finish if (captureThread_.joinable()) { captureThread_.join(); @@ -78,41 +78,44 @@ void RealtimeProcessor::stop() { if (processingThread_.joinable()) { processingThread_.join(); } - + // Cleanup resources cleanupCapture(); - + // Clear frame buffer std::lock_guard lock(frameMutex_); frameBuffer_.clear(); } -blob RealtimeProcessor::processFrame(const blob& input, const FrameInfo& frameInfo) { +blob RealtimeProcessor::processFrame(const blob& input, + const FrameInfo& frameInfo) { if (input.empty()) { return blob{}; } - + auto startTime = std::chrono::high_resolution_clock::now(); - + // Apply processing pipeline blob result = applyProcessingPipeline(input, frameInfo); - + auto endTime = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration_cast(endTime - startTime); - + auto duration = std::chrono::duration_cast( + endTime - startTime); + // Update statistics updateStatistics(static_cast(duration.count())); - + return result; } -bool RealtimeProcessor::addFrame(const blob& frame, const FrameInfo& frameInfo) { +bool RealtimeProcessor::addFrame(const blob& frame, + const FrameInfo& frameInfo) { if (frame.empty()) { return false; } - + std::lock_guard lock(frameMutex_); - + // Check buffer size if (static_cast(frameBuffer_.size()) >= maxBufferSize_) { if (enableFrameDropping_) { @@ -120,25 +123,27 @@ bool RealtimeProcessor::addFrame(const blob& frame, const FrameInfo& frameInfo) frameBuffer_.pop(); stats_.droppedFrames++; } else { - return false; // Buffer full + return false; // Buffer full } } - + // Add frame to buffer frameBuffer_.push({frame, frameInfo}); frameCondition_.notify_one(); - + return true; } -void RealtimeProcessor::setProcessingMode(ProcessingMode mode, - const std::unordered_map& params) { +void RealtimeProcessor::setProcessingMode( + ProcessingMode mode, + const std::unordered_map& params) { processingMode_ = mode; modeParams_ = params; } -void RealtimeProcessor::addFilter(const std::string& filterName, - const std::unordered_map& params) { +void RealtimeProcessor::addFilter( + const std::string& filterName, + const std::unordered_map& params) { std::lock_guard lock(filterMutex_); filters_[filterName] = params; } @@ -161,65 +166,54 @@ void RealtimeProcessor::setAnalysisCallback(AnalysisCallback callback) { analysisCallback_ = callback; } -ProcessingStats RealtimeProcessor::getStatistics() const { - return stats_; -} +ProcessingStats RealtimeProcessor::getStatistics() const { return stats_; } -double RealtimeProcessor::getCurrentFPS() const { - return stats_.currentFPS; -} +double RealtimeProcessor::getCurrentFPS() const { return stats_.currentFPS; } -double RealtimeProcessor::getLatency() const { - return stats_.averageLatency; -} +double RealtimeProcessor::getLatency() const { return stats_.averageLatency; } -bool RealtimeProcessor::isRunning() const { - return running_.load(); -} +bool RealtimeProcessor::isRunning() const { return running_.load(); } -void RealtimeProcessor::pause() { - paused_.store(true); -} +void RealtimeProcessor::pause() { paused_.store(true); } void RealtimeProcessor::resume() { paused_.store(false); frameCondition_.notify_all(); } -bool RealtimeProcessor::isPaused() const { - return paused_.load(); -} +bool RealtimeProcessor::isPaused() const { return paused_.load(); } bool RealtimeProcessor::startRecording(const std::string& outputPath, - const std::string& codec, - int quality) { + const std::string& codec, int quality) { if (recording_.load()) { - return false; // Already recording + return false; // Already recording } - + try { #ifdef ATOM_IMAGE_HAS_OPENCV // Initialize video writer - int fourcc = cv::VideoWriter::fourcc('H', '2', '6', '4'); // Default to H264 + int fourcc = + cv::VideoWriter::fourcc('H', '2', '6', '4'); // Default to H264 if (codec == "h265") { fourcc = cv::VideoWriter::fourcc('H', '2', '6', '5'); } else if (codec == "vp9") { fourcc = cv::VideoWriter::fourcc('V', 'P', '0', '9'); } - + // Use current capture resolution or default - cv::Size frameSize(1920, 1080); // Default resolution - - videoWriter_ = std::make_unique(outputPath, fourcc, targetFPS_, frameSize); - + cv::Size frameSize(1920, 1080); // Default resolution + + videoWriter_ = std::make_unique(outputPath, fourcc, + targetFPS_, frameSize); + if (!videoWriter_->isOpened()) { return false; } - + recording_.store(true); return true; #else - return false; // OpenCV required for recording + return false; // OpenCV required for recording #endif } catch (const std::exception&) { return false; @@ -230,9 +224,9 @@ void RealtimeProcessor::stopRecording() { if (!recording_.load()) { return; } - + recording_.store(false); - + #ifdef ATOM_IMAGE_HAS_OPENCV if (videoWriter_) { videoWriter_->release(); @@ -241,41 +235,35 @@ void RealtimeProcessor::stopRecording() { #endif } -bool RealtimeProcessor::isRecording() const { - return recording_.load(); -} +bool RealtimeProcessor::isRecording() const { return recording_.load(); } bool RealtimeProcessor::takeSnapshot(const std::string& outputPath) { if (!running_.load() || frameBuffer_.empty()) { return false; } - + try { std::lock_guard lock(frameMutex_); if (!frameBuffer_.empty()) { const auto& frameData = frameBuffer_.back(); - + #ifdef ATOM_IMAGE_HAS_OPENCV cv::Mat frame = frameData.first.to_mat(); return cv::imwrite(outputPath, frame); #else - return false; // OpenCV required for snapshot + return false; // OpenCV required for snapshot #endif } } catch (const std::exception&) { return false; } - + return false; } -void RealtimeProcessor::setTargetFPS(double fps) { - targetFPS_ = fps; -} +void RealtimeProcessor::setTargetFPS(double fps) { targetFPS_ = fps; } -void RealtimeProcessor::setMaxBufferSize(int size) { - maxBufferSize_ = size; -} +void RealtimeProcessor::setMaxBufferSize(int size) { maxBufferSize_ = size; } void RealtimeProcessor::setFrameDropping(bool enable) { enableFrameDropping_ = enable; @@ -283,7 +271,7 @@ void RealtimeProcessor::setFrameDropping(bool enable) { std::vector RealtimeProcessor::getAvailableDevices() const { std::vector devices; - + #ifdef ATOM_IMAGE_HAS_OPENCV // Try to enumerate camera devices for (int i = 0; i < 10; ++i) { @@ -294,11 +282,12 @@ std::vector RealtimeProcessor::getAvailableDevices() const { } } #endif - + return devices; } -std::vector RealtimeProcessor::getSupportedFormats(const std::string& devicePath) const { +std::vector RealtimeProcessor::getSupportedFormats( + const std::string& devicePath) const { // Return common video formats return {"BGR", "RGB", "GRAY", "YUV420", "MJPEG", "H264"}; } @@ -306,7 +295,7 @@ std::vector RealtimeProcessor::getSupportedFormats(const std::strin bool RealtimeProcessor::setCaptureResolution(int width, int height) { captureWidth_ = width; captureHeight_ = height; - + #ifdef ATOM_IMAGE_HAS_OPENCV if (capture_) { capture_->set(cv::CAP_PROP_FRAME_WIDTH, width); @@ -314,20 +303,20 @@ bool RealtimeProcessor::setCaptureResolution(int width, int height) { return true; } #endif - + return false; } bool RealtimeProcessor::setCaptureFPS(double fps) { captureFPS_ = fps; - + #ifdef ATOM_IMAGE_HAS_OPENCV if (capture_) { capture_->set(cv::CAP_PROP_FPS, fps); return true; } #endif - + return false; } @@ -339,7 +328,8 @@ void RealtimeProcessor::processingThread() { } std::unique_lock lock(frameMutex_); - frameCondition_.wait(lock, [this] { return !frameBuffer_.empty() || !running_.load(); }); + frameCondition_.wait( + lock, [this] { return !frameBuffer_.empty() || !running_.load(); }); if (!running_.load()) { break; @@ -377,7 +367,7 @@ void RealtimeProcessor::processingThread() { void RealtimeProcessor::captureThread() { auto lastFrameTime = std::chrono::high_resolution_clock::now(); - double frameInterval = 1000.0 / targetFPS_; // milliseconds + double frameInterval = 1000.0 / targetFPS_; // milliseconds while (running_.load()) { if (paused_.load()) { @@ -386,7 +376,8 @@ void RealtimeProcessor::captureThread() { } auto currentTime = std::chrono::high_resolution_clock::now(); - auto elapsed = std::chrono::duration_cast(currentTime - lastFrameTime); + auto elapsed = std::chrono::duration_cast( + currentTime - lastFrameTime); if (elapsed.count() < frameInterval) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); @@ -399,8 +390,10 @@ void RealtimeProcessor::captureThread() { if (capture_->read(frame)) { if (!frame.empty()) { FrameInfo frameInfo; - frameInfo.timestamp = std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()).count(); + frameInfo.timestamp = + std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); frameInfo.frameNumber = stats_.capturedFrames; blob frameBlob(frame); @@ -415,7 +408,8 @@ void RealtimeProcessor::captureThread() { } } -blob RealtimeProcessor::applyProcessingPipeline(const blob& input, const FrameInfo& frameInfo) { +blob RealtimeProcessor::applyProcessingPipeline(const blob& input, + const FrameInfo& frameInfo) { if (input.empty()) { return blob{}; } @@ -462,17 +456,21 @@ blob RealtimeProcessor::applyProcessingPipeline(const blob& input, const FrameIn void RealtimeProcessor::updateStatistics(double processingTime) { stats_.totalProcessingTime += processingTime; - stats_.averageLatency = stats_.totalProcessingTime / std::max(1UL, stats_.processedFrames); + stats_.averageLatency = + stats_.totalProcessingTime / std::max(1UL, stats_.processedFrames); // Calculate FPS auto currentTime = std::chrono::high_resolution_clock::now(); - auto elapsed = std::chrono::duration_cast(currentTime - stats_.startTime); + auto elapsed = std::chrono::duration_cast( + currentTime - stats_.startTime); if (elapsed.count() > 0) { - stats_.currentFPS = static_cast(stats_.processedFrames) / elapsed.count(); + stats_.currentFPS = + static_cast(stats_.processedFrames) / elapsed.count(); } } -bool RealtimeProcessor::initializeCapture(CaptureSource source, const std::string& sourcePath) { +bool RealtimeProcessor::initializeCapture(CaptureSource source, + const std::string& sourcePath) { #ifdef ATOM_IMAGE_HAS_OPENCV try { switch (source) { @@ -518,7 +516,7 @@ bool RealtimeProcessor::initializeCapture(CaptureSource source, const std::strin return false; } #else - return false; // OpenCV required for capture + return false; // OpenCV required for capture #endif } @@ -540,7 +538,7 @@ blob RealtimeProcessor::resizeFrame(const blob& input) { cv::Mat src = input.to_mat(); cv::Mat dst; cv::Size targetSize(captureWidth_ > 0 ? captureWidth_ : src.cols, - captureHeight_ > 0 ? captureHeight_ : src.rows); + captureHeight_ > 0 ? captureHeight_ : src.rows); cv::resize(src, dst, targetSize); return blob(dst); #else @@ -548,7 +546,8 @@ blob RealtimeProcessor::resizeFrame(const blob& input) { #endif } -blob RealtimeProcessor::convertFormat(const blob& input, const std::string& targetFormat) { +blob RealtimeProcessor::convertFormat(const blob& input, + const std::string& targetFormat) { if (input.empty()) { return input; } @@ -572,8 +571,9 @@ blob RealtimeProcessor::convertFormat(const blob& input, const std::string& targ } // Helper methods for processing pipeline -blob RealtimeProcessor::applyFilter(const blob& input, const std::string& filterName, - const std::unordered_map& params) { +blob RealtimeProcessor::applyFilter( + const blob& input, const std::string& filterName, + const std::unordered_map& params) { if (input.empty()) { return input; } @@ -583,14 +583,17 @@ blob RealtimeProcessor::applyFilter(const blob& input, const std::string& filter cv::Mat dst; if (filterName == "blur") { - int kernelSize = static_cast(params.count("kernel_size") ? params.at("kernel_size") : 5); + int kernelSize = static_cast( + params.count("kernel_size") ? params.at("kernel_size") : 5); cv::blur(src, dst, cv::Size(kernelSize, kernelSize)); } else if (filterName == "gaussian_blur") { - int kernelSize = static_cast(params.count("kernel_size") ? params.at("kernel_size") : 5); + int kernelSize = static_cast( + params.count("kernel_size") ? params.at("kernel_size") : 5); double sigma = params.count("sigma") ? params.at("sigma") : 1.0; cv::GaussianBlur(src, dst, cv::Size(kernelSize, kernelSize), sigma); } else if (filterName == "sharpen") { - cv::Mat kernel = (cv::Mat_(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0); + cv::Mat kernel = + (cv::Mat_(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0); cv::filter2D(src, dst, -1, kernel); } else { dst = src.clone(); @@ -634,13 +637,15 @@ blob RealtimeProcessor::applyEnhancement(const blob& input) { #endif } -blob RealtimeProcessor::applyDetection(const blob& input, const FrameInfo& frameInfo) { +blob RealtimeProcessor::applyDetection(const blob& input, + const FrameInfo& frameInfo) { // Placeholder for object detection // In a real implementation, this would run object detection models return input; } -blob RealtimeProcessor::applyTracking(const blob& input, const FrameInfo& frameInfo) { +blob RealtimeProcessor::applyTracking(const blob& input, + const FrameInfo& frameInfo) { // Placeholder for object tracking // In a real implementation, this would track objects across frames return input; diff --git a/atom/image/processing/realtime.hpp b/atom/image/processing/realtime.hpp index 5bbaedc1..70bac24c 100644 --- a/atom/image/processing/realtime.hpp +++ b/atom/image/processing/realtime.hpp @@ -14,16 +14,16 @@ * @version 1.0.0 */ -#include "../core/image_blob.hpp" -#include -#include -#include -#include #include -#include -#include -#include #include +#include +#include +#include +#include +#include +#include +#include +#include "../core/image_blob.hpp" namespace atom::image { @@ -31,54 +31,54 @@ namespace atom::image { * @brief Video capture sources */ enum class CaptureSource { - CAMERA, // Camera device - FILE, // Video file - STREAM, // Network stream (RTSP, HTTP, etc.) - SCREEN, // Screen capture - SYNTHETIC, // Synthetic/generated frames - CUSTOM // Custom source + CAMERA, // Camera device + FILE, // Video file + STREAM, // Network stream (RTSP, HTTP, etc.) + SCREEN, // Screen capture + SYNTHETIC, // Synthetic/generated frames + CUSTOM // Custom source }; /** * @brief Real-time processing modes */ enum class ProcessingMode { - PASSTHROUGH, // No processing (passthrough) - FILTER, // Apply filters - ENHANCE, // Image enhancement - DETECT, // Object/feature detection - TRACK, // Object tracking - ANALYZE, // Image analysis - CUSTOM // Custom processing pipeline + PASSTHROUGH, // No processing (passthrough) + FILTER, // Apply filters + ENHANCE, // Image enhancement + DETECT, // Object/feature detection + TRACK, // Object tracking + ANALYZE, // Image analysis + CUSTOM // Custom processing pipeline }; /** * @brief Frame information */ struct FrameInfo { - int64_t timestamp; // Frame timestamp (microseconds) - int frameNumber; // Frame sequence number - double fps; // Current FPS - int width, height; // Frame dimensions - int channels; // Number of channels - std::string format; // Pixel format - std::unordered_map metadata; // Additional metadata + int64_t timestamp; // Frame timestamp (microseconds) + int frameNumber; // Frame sequence number + double fps; // Current FPS + int width, height; // Frame dimensions + int channels; // Number of channels + std::string format; // Pixel format + std::unordered_map metadata; // Additional metadata }; /** * @brief Processing statistics */ struct ProcessingStats { - double averageFPS = 0.0; // Average processing FPS - double currentFPS = 0.0; // Current processing FPS - double averageLatency = 0.0; // Average processing latency (ms) - double currentLatency = 0.0; // Current processing latency (ms) - int64_t framesProcessed = 0; // Total frames processed - int64_t framesDropped = 0; // Total frames dropped - double cpuUsage = 0.0; // CPU usage percentage - double memoryUsage = 0.0; // Memory usage (MB) - double gpuUsage = 0.0; // GPU usage percentage - std::string status = "idle"; // Current status + double averageFPS = 0.0; // Average processing FPS + double currentFPS = 0.0; // Current processing FPS + double averageLatency = 0.0; // Average processing latency (ms) + double currentLatency = 0.0; // Current processing latency (ms) + int64_t framesProcessed = 0; // Total frames processed + int64_t framesDropped = 0; // Total frames dropped + double cpuUsage = 0.0; // CPU usage percentage + double memoryUsage = 0.0; // Memory usage (MB) + double gpuUsage = 0.0; // GPU usage percentage + std::string status = "idle"; // Current status }; /** @@ -86,28 +86,28 @@ struct ProcessingStats { */ struct RealtimeParams { // Performance parameters - int maxBufferSize = 5; // Maximum frame buffer size - int numThreads = 0; // Number of processing threads (0 = auto) - bool useGPU = true; // Use GPU acceleration - bool dropFrames = true; // Drop frames if processing is slow - double targetFPS = 30.0; // Target processing FPS + int maxBufferSize = 5; // Maximum frame buffer size + int numThreads = 0; // Number of processing threads (0 = auto) + bool useGPU = true; // Use GPU acceleration + bool dropFrames = true; // Drop frames if processing is slow + double targetFPS = 30.0; // Target processing FPS // Quality parameters - int maxWidth = 1920; // Maximum frame width - int maxHeight = 1080; // Maximum frame height - bool maintainAspectRatio = true; // Maintain aspect ratio when resizing - std::string pixelFormat = "RGB"; // Preferred pixel format + int maxWidth = 1920; // Maximum frame width + int maxHeight = 1080; // Maximum frame height + bool maintainAspectRatio = true; // Maintain aspect ratio when resizing + std::string pixelFormat = "RGB"; // Preferred pixel format // Processing parameters ProcessingMode mode = ProcessingMode::PASSTHROUGH; - std::vector filters; // Filters to apply - std::unordered_map filterParams; // Filter parameters + std::vector filters; // Filters to apply + std::unordered_map filterParams; // Filter parameters // Callback parameters - bool enablePreview = true; // Enable preview callbacks - bool enableAnalysis = false; // Enable analysis callbacks - bool enableRecording = false; // Enable recording - std::string recordingPath; // Recording output path + bool enablePreview = true; // Enable preview callbacks + bool enableAnalysis = false; // Enable analysis callbacks + bool enableRecording = false; // Enable recording + std::string recordingPath; // Recording output path }; /** @@ -118,7 +118,8 @@ using FrameCallback = std::function; /** * @brief Analysis callback function type */ -using AnalysisCallback = std::function&)>; +using AnalysisCallback = + std::function&)>; /** * @brief Real-time image processor @@ -144,9 +145,9 @@ class RealtimeProcessor { * @return Success status */ virtual bool startCapture(CaptureSource source, - const std::string& sourcePath, - FrameCallback frameCallback = nullptr, - AnalysisCallback analysisCallback = nullptr); + const std::string& sourcePath, + FrameCallback frameCallback = nullptr, + AnalysisCallback analysisCallback = nullptr); /** * @brief Stop processing and capture @@ -159,7 +160,8 @@ class RealtimeProcessor { * @param frameInfo Frame information * @return Processed frame */ - virtual blob processFrame(const blob& input, const FrameInfo& frameInfo = {}); + virtual blob processFrame(const blob& input, + const FrameInfo& frameInfo = {}); /** * @brief Add frame to processing queue @@ -174,16 +176,18 @@ class RealtimeProcessor { * @param mode Processing mode * @param params Mode-specific parameters */ - virtual void setProcessingMode(ProcessingMode mode, - const std::unordered_map& params = {}); + virtual void setProcessingMode( + ProcessingMode mode, + const std::unordered_map& params = {}); /** * @brief Add processing filter * @param filterName Filter name * @param params Filter parameters */ - virtual void addFilter(const std::string& filterName, - const std::unordered_map& params = {}); + virtual void addFilter( + const std::string& filterName, + const std::unordered_map& params = {}); /** * @brief Remove processing filter @@ -256,8 +260,8 @@ class RealtimeProcessor { * @return Success status */ virtual bool startRecording(const std::string& outputPath, - const std::string& codec = "h264", - int quality = 80); + const std::string& codec = "h264", + int quality = 80); /** * @brief Stop recording @@ -306,7 +310,8 @@ class RealtimeProcessor { * @param devicePath Device path or index * @return Vector of supported formats */ - virtual std::vector getSupportedFormats(const std::string& devicePath) const; + virtual std::vector getSupportedFormats( + const std::string& devicePath) const; /** * @brief Set capture resolution @@ -340,7 +345,8 @@ class RealtimeProcessor { * @param frameInfo Frame information * @return Processed frame */ - virtual blob applyProcessingPipeline(const blob& input, const FrameInfo& frameInfo); + virtual blob applyProcessingPipeline(const blob& input, + const FrameInfo& frameInfo); /** * @brief Update processing statistics @@ -354,7 +360,8 @@ class RealtimeProcessor { * @param sourcePath Source path * @return Success status */ - virtual bool initializeCapture(CaptureSource source, const std::string& sourcePath); + virtual bool initializeCapture(CaptureSource source, + const std::string& sourcePath); /** * @brief Cleanup capture resources @@ -374,7 +381,8 @@ class RealtimeProcessor { * @param targetFormat Target pixel format * @return Converted frame */ - virtual blob convertFormat(const blob& input, const std::string& targetFormat); + virtual blob convertFormat(const blob& input, + const std::string& targetFormat); private: // Thread management @@ -410,8 +418,9 @@ class RealtimeProcessor { * @param numThreads Number of processing threads (0 = auto) * @return Unique pointer to real-time processor */ -std::unique_ptr createOptimalRealtimeProcessor(bool useGPU = true, int numThreads = 0); +std::unique_ptr createOptimalRealtimeProcessor( + bool useGPU = true, int numThreads = 0); -} // namespace atom::image +} // namespace atom::image -#endif // ATOM_IMAGE_REALTIME_HPP +#endif // ATOM_IMAGE_REALTIME_HPP diff --git a/atom/image/processing/realtime_stub.cpp b/atom/image/processing/realtime_stub.cpp index b605e205..5b0da4fa 100644 --- a/atom/image/processing/realtime_stub.cpp +++ b/atom/image/processing/realtime_stub.cpp @@ -12,9 +12,7 @@ namespace { constexpr double kMillisInSecond = 1000.0; -auto clampPositive(double value) -> double { - return value < 0.0 ? 0.0 : value; -} +auto clampPositive(double value) -> double { return value < 0.0 ? 0.0 : value; } auto currentTimeMilliseconds() -> double { using clock = std::chrono::high_resolution_clock; @@ -23,7 +21,7 @@ auto currentTimeMilliseconds() -> double { return std::chrono::duration(epoch).count(); } -} // namespace +} // namespace bool RealtimeProcessor::initialize(const RealtimeParams& params) { { @@ -47,9 +45,9 @@ bool RealtimeProcessor::initialize(const RealtimeParams& params) { } bool RealtimeProcessor::startCapture(CaptureSource source, - const std::string& sourcePath, - FrameCallback frameCallback, - AnalysisCallback analysisCallback) { + const std::string& sourcePath, + FrameCallback frameCallback, + AnalysisCallback analysisCallback) { if (frameCallback) { frameCallback_ = std::move(frameCallback); } @@ -91,7 +89,8 @@ void RealtimeProcessor::stop() { } } -blob RealtimeProcessor::processFrame(const blob& input, const FrameInfo& frameInfo) { +blob RealtimeProcessor::processFrame(const blob& input, + const FrameInfo& frameInfo) { if (input.isEmpty()) { return {}; } @@ -118,7 +117,8 @@ blob RealtimeProcessor::processFrame(const blob& input, const FrameInfo& frameIn return output; } -bool RealtimeProcessor::addFrame(const blob& frame, const FrameInfo& frameInfo) { +bool RealtimeProcessor::addFrame(const blob& frame, + const FrameInfo& frameInfo) { if (frame.isEmpty()) { return false; } @@ -150,16 +150,18 @@ bool RealtimeProcessor::addFrame(const blob& frame, const FrameInfo& frameInfo) return true; } -void RealtimeProcessor::setProcessingMode(ProcessingMode mode, - const std::unordered_map& params) { +void RealtimeProcessor::setProcessingMode( + ProcessingMode mode, + const std::unordered_map& params) { params_.mode = mode; for (const auto& [key, value] : params) { params_.filterParams[key] = value; } } -void RealtimeProcessor::addFilter(const std::string& filterName, - const std::unordered_map& params) { +void RealtimeProcessor::addFilter( + const std::string& filterName, + const std::unordered_map& params) { params_.filters.push_back(filterName); for (const auto& [key, value] : params) { params_.filterParams[key] = value; @@ -168,7 +170,8 @@ void RealtimeProcessor::addFilter(const std::string& filterName, void RealtimeProcessor::removeFilter(const std::string& filterName) { auto& filters = params_.filters; - filters.erase(std::remove(filters.begin(), filters.end(), filterName), filters.end()); + filters.erase(std::remove(filters.begin(), filters.end(), filterName), + filters.end()); } void RealtimeProcessor::clearFilters() { @@ -199,9 +202,7 @@ double RealtimeProcessor::getLatency() const { return stats_.currentLatency; } -bool RealtimeProcessor::isRunning() const { - return running_.load(); -} +bool RealtimeProcessor::isRunning() const { return running_.load(); } void RealtimeProcessor::pause() { paused_.store(true); @@ -215,13 +216,11 @@ void RealtimeProcessor::resume() { stats_.status = running_.load() ? "capturing" : "idle"; } -bool RealtimeProcessor::isPaused() const { - return paused_.load(); -} +bool RealtimeProcessor::isPaused() const { return paused_.load(); } bool RealtimeProcessor::startRecording(const std::string& outputPath, - const std::string& /*codec*/, - int /*quality*/) { + const std::string& /*codec*/, + int /*quality*/) { if (outputPath.empty()) { return false; } @@ -241,17 +240,13 @@ void RealtimeProcessor::stopRecording() { stats_.status = running_.load() ? "capturing" : "idle"; } -bool RealtimeProcessor::isRecording() const { - return recording_.load(); -} +bool RealtimeProcessor::isRecording() const { return recording_.load(); } bool RealtimeProcessor::takeSnapshot(const std::string& outputPath) { return !outputPath.empty(); } -void RealtimeProcessor::setTargetFPS(double fps) { - params_.targetFPS = fps; -} +void RealtimeProcessor::setTargetFPS(double fps) { params_.targetFPS = fps; } void RealtimeProcessor::setMaxBufferSize(int size) { params_.maxBufferSize = size; @@ -265,7 +260,8 @@ std::vector RealtimeProcessor::getAvailableDevices() const { return {"synthetic"}; } -std::vector RealtimeProcessor::getSupportedFormats(const std::string& /*devicePath*/) const { +std::vector RealtimeProcessor::getSupportedFormats( + const std::string& /*devicePath*/) const { return {"RGB", "BGR"}; } @@ -284,7 +280,8 @@ void RealtimeProcessor::processingThread() {} void RealtimeProcessor::captureThread() {} -blob RealtimeProcessor::applyProcessingPipeline(const blob& input, const FrameInfo& /*frameInfo*/) { +blob RealtimeProcessor::applyProcessingPipeline( + const blob& input, const FrameInfo& /*frameInfo*/) { blob output = input; return output; } @@ -300,15 +297,21 @@ void RealtimeProcessor::updateStatistics(double processingTime) { stats_.averageLatency = stats_.currentLatency; } else { stats_.averageLatency = - ((stats_.averageLatency * (count - 1.0)) + stats_.currentLatency) / count; + ((stats_.averageLatency * (count - 1.0)) + stats_.currentLatency) / + count; } - stats_.currentFPS = stats_.currentLatency > 0.0 ? kMillisInSecond / stats_.currentLatency : 0.0; + stats_.currentFPS = stats_.currentLatency > 0.0 + ? kMillisInSecond / stats_.currentLatency + : 0.0; stats_.averageFPS = stats_.framesProcessed > 0 ? stats_.currentFPS : 0.0; - stats_.status = running_.load() ? "capturing" : stats_.status.empty() ? "idle" : stats_.status; + stats_.status = running_.load() ? "capturing" + : stats_.status.empty() ? "idle" + : stats_.status; } -bool RealtimeProcessor::initializeCapture(CaptureSource source, const std::string& /*sourcePath*/) { +bool RealtimeProcessor::initializeCapture(CaptureSource source, + const std::string& /*sourcePath*/) { return source == CaptureSource::SYNTHETIC; } @@ -322,7 +325,8 @@ blob RealtimeProcessor::resizeFrame(const blob& input) { return output; } -blob RealtimeProcessor::convertFormat(const blob& input, const std::string& /*targetFormat*/) { +blob RealtimeProcessor::convertFormat(const blob& input, + const std::string& /*targetFormat*/) { if (input.isEmpty()) { return {}; } @@ -330,8 +334,9 @@ blob RealtimeProcessor::convertFormat(const blob& input, const std::string& /*ta return output; } -std::unique_ptr createOptimalRealtimeProcessor(bool /*useGPU*/, int /*numThreads*/) { +std::unique_ptr createOptimalRealtimeProcessor( + bool /*useGPU*/, int /*numThreads*/) { return std::make_unique(); } -} // namespace atom::image +} // namespace atom::image diff --git a/atom/image/processing/transforms.hpp b/atom/image/processing/transforms.hpp index 7a8ccdf2..f48407e8 100644 --- a/atom/image/processing/transforms.hpp +++ b/atom/image/processing/transforms.hpp @@ -5,20 +5,20 @@ * @file transforms.hpp * @brief Advanced image transformation operations * - * This module provides comprehensive image transformation capabilities including - * geometric transformations, perspective corrections, image registration, - * and advanced warping operations. + * This module provides comprehensive image transformation capabilities + * including geometric transformations, perspective corrections, image + * registration, and advanced warping operations. * * @author Atom Framework Team * @date 2025 * @version 1.0.0 */ -#include "../core/image_blob.hpp" -#include #include -#include #include +#include +#include +#include "../core/image_blob.hpp" namespace atom::image { @@ -38,11 +38,11 @@ enum class InterpolationMethod { * @brief Border handling modes for transformations */ enum class BorderMode { - CONSTANT, // Fill with constant value - REPLICATE, // Replicate edge pixels - REFLECT, // Reflect across edge - WRAP, // Wrap around - TRANSPARENT // Transparent (for alpha channel) + CONSTANT, // Fill with constant value + REPLICATE, // Replicate edge pixels + REFLECT, // Reflect across edge + WRAP, // Wrap around + TRANSPARENT // Transparent (for alpha channel) }; /** @@ -84,9 +84,10 @@ class ImageTransform { * @param preserveAspect Whether to preserve aspect ratio * @return Resized image blob */ - virtual blob resize(const blob& input, int newWidth, int newHeight, - InterpolationMethod method = InterpolationMethod::LINEAR, - bool preserveAspect = false) const; + virtual blob resize( + const blob& input, int newWidth, int newHeight, + InterpolationMethod method = InterpolationMethod::LINEAR, + bool preserveAspect = false) const; /** * @brief Rotate image by arbitrary angle @@ -99,12 +100,12 @@ class ImageTransform { * @param fillValue Fill value for constant border mode * @return Rotated image blob */ - virtual blob rotate(const blob& input, double angle, - const Point2D& center = {}, - bool expandCanvas = true, - InterpolationMethod method = InterpolationMethod::LINEAR, - BorderMode borderMode = BorderMode::CONSTANT, - uint8_t fillValue = 0) const; + virtual blob rotate( + const blob& input, double angle, const Point2D& center = {}, + bool expandCanvas = true, + InterpolationMethod method = InterpolationMethod::LINEAR, + BorderMode borderMode = BorderMode::CONSTANT, + uint8_t fillValue = 0) const; /** * @brief Apply affine transformation @@ -115,11 +116,11 @@ class ImageTransform { * @param borderMode Border handling mode * @return Transformed image blob */ - virtual blob affineTransform(const blob& input, - const std::array, 2>& matrix, - const Point2D& outputSize = {}, - InterpolationMethod method = InterpolationMethod::LINEAR, - BorderMode borderMode = BorderMode::CONSTANT) const; + virtual blob affineTransform( + const blob& input, const std::array, 2>& matrix, + const Point2D& outputSize = {}, + InterpolationMethod method = InterpolationMethod::LINEAR, + BorderMode borderMode = BorderMode::CONSTANT) const; /** * @brief Apply perspective transformation @@ -129,10 +130,10 @@ class ImageTransform { * @param method Interpolation method * @return Transformed image blob */ - virtual blob perspectiveTransform(const blob& input, - const TransformMatrix& matrix, - const Point2D& outputSize, - InterpolationMethod method = InterpolationMethod::LINEAR) const; + virtual blob perspectiveTransform( + const blob& input, const TransformMatrix& matrix, + const Point2D& outputSize, + InterpolationMethod method = InterpolationMethod::LINEAR) const; /** * @brief Correct perspective distortion using four corner points @@ -143,9 +144,9 @@ class ImageTransform { * @return Perspective-corrected image blob */ virtual blob correctPerspective(const blob& input, - const std::array& srcPoints, - const std::array& dstPoints, - const Point2D& outputSize) const; + const std::array& srcPoints, + const std::array& dstPoints, + const Point2D& outputSize) const; /** * @brief Apply barrel/pincushion distortion correction @@ -158,10 +159,9 @@ class ImageTransform { * @param center Distortion center (if empty, use image center) * @return Distortion-corrected image blob */ - virtual blob correctDistortion(const blob& input, - double k1, double k2 = 0, double k3 = 0, - double p1 = 0, double p2 = 0, - const Point2D& center = {}) const; + virtual blob correctDistortion(const blob& input, double k1, double k2 = 0, + double k3 = 0, double p1 = 0, double p2 = 0, + const Point2D& center = {}) const; /** * @brief Apply elastic deformation (rubber sheet transformation) @@ -171,10 +171,11 @@ class ImageTransform { * @param method Interpolation method * @return Deformed image blob */ - virtual blob elasticDeform(const blob& input, - const std::vector>& displacementX, - const std::vector>& displacementY, - InterpolationMethod method = InterpolationMethod::LINEAR) const; + virtual blob elasticDeform( + const blob& input, + const std::vector>& displacementX, + const std::vector>& displacementY, + InterpolationMethod method = InterpolationMethod::LINEAR) const; /** * @brief Apply polar transformation (Cartesian to polar coordinates) @@ -184,10 +185,8 @@ class ImageTransform { * @param angleRange Angle range in degrees (default: full circle) * @return Polar-transformed image blob */ - virtual blob toPolar(const blob& input, - const Point2D& center = {}, - double maxRadius = 0, - double angleRange = 360.0) const; + virtual blob toPolar(const blob& input, const Point2D& center = {}, + double maxRadius = 0, double angleRange = 360.0) const; /** * @brief Apply inverse polar transformation (polar to Cartesian) @@ -196,9 +195,8 @@ class ImageTransform { * @param center Center point for transformation * @return Cartesian-transformed image blob */ - virtual blob fromPolar(const blob& input, - const Point2D& outputSize, - const Point2D& center = {}) const; + virtual blob fromPolar(const blob& input, const Point2D& outputSize, + const Point2D& center = {}) const; /** * @brief Register two images using feature matching @@ -207,9 +205,9 @@ class ImageTransform { * @param method Registration method * @return Transformation matrix to align target with reference */ - virtual TransformMatrix registerImages(const blob& reference, - const blob& target, - const std::string& method = "orb") const; + virtual TransformMatrix registerImages( + const blob& reference, const blob& target, + const std::string& method = "orb") const; /** * @brief Apply image warping using control points @@ -219,10 +217,10 @@ class ImageTransform { * @param method Warping method ("thin_plate_spline", "rbf", "polynomial") * @return Warped image blob */ - virtual blob warpControlPoints(const blob& input, - const std::vector& srcPoints, - const std::vector& dstPoints, - const std::string& method = "thin_plate_spline") const; + virtual blob warpControlPoints( + const blob& input, const std::vector& srcPoints, + const std::vector& dstPoints, + const std::string& method = "thin_plate_spline") const; /** * @brief Create seamless panorama from multiple images @@ -231,9 +229,10 @@ class ImageTransform { * @param blendMode Blending mode ("linear", "multiband", "feather") * @return Stitched panorama image */ - virtual blob stitchPanorama(const std::vector& images, - const std::string& method = "cylindrical", - const std::string& blendMode = "multiband") const; + virtual blob stitchPanorama( + const std::vector& images, + const std::string& method = "cylindrical", + const std::string& blendMode = "multiband") const; // Static utility functions @@ -257,7 +256,8 @@ class ImageTransform { * @param center Rotation center * @return Rotation matrix */ - static TransformMatrix createRotationMatrix(double angle, const Point2D& center = {}); + static TransformMatrix createRotationMatrix(double angle, + const Point2D& center = {}); /** * @brief Create scaling matrix @@ -266,7 +266,8 @@ class ImageTransform { * @param center Scaling center * @return Scaling matrix */ - static TransformMatrix createScalingMatrix(double sx, double sy, const Point2D& center = {}); + static TransformMatrix createScalingMatrix(double sx, double sy, + const Point2D& center = {}); /** * @brief Create shear matrix @@ -282,7 +283,8 @@ class ImageTransform { * @param b Second matrix * @return Product matrix (a * b) */ - static TransformMatrix multiplyMatrices(const TransformMatrix& a, const TransformMatrix& b); + static TransformMatrix multiplyMatrices(const TransformMatrix& a, + const TransformMatrix& b); /** * @brief Invert transformation matrix @@ -297,23 +299,25 @@ class ImageTransform { * @param matrix Transformation matrix * @return Transformed point */ - static Point2D transformPoint(const Point2D& point, const TransformMatrix& matrix); + static Point2D transformPoint(const Point2D& point, + const TransformMatrix& matrix); protected: /** * @brief Apply generic transformation with custom mapping function * @param input Input image blob * @param outputSize Output image size - * @param mapFunction Function that maps output coordinates to input coordinates + * @param mapFunction Function that maps output coordinates to input + * coordinates * @param method Interpolation method * @param borderMode Border handling mode * @return Transformed image blob */ - virtual blob applyTransformation(const blob& input, - const Point2D& outputSize, - std::function mapFunction, - InterpolationMethod method = InterpolationMethod::LINEAR, - BorderMode borderMode = BorderMode::CONSTANT) const; + virtual blob applyTransformation( + const blob& input, const Point2D& outputSize, + std::function mapFunction, + InterpolationMethod method = InterpolationMethod::LINEAR, + BorderMode borderMode = BorderMode::CONSTANT) const; /** * @brief Interpolate pixel value at fractional coordinates @@ -327,11 +331,10 @@ class ImageTransform { * @param borderMode Border handling mode * @return Interpolated pixel values */ - virtual std::vector interpolatePixel(const std::vector& input, - double x, double y, - int width, int height, int channels, - InterpolationMethod method, - BorderMode borderMode) const; + virtual std::vector interpolatePixel( + const std::vector& input, double x, double y, int width, + int height, int channels, InterpolationMethod method, + BorderMode borderMode) const; }; /** @@ -341,6 +344,6 @@ class ImageTransform { */ std::unique_ptr createOptimalTransform(bool useGPU = false); -} // namespace atom::image +} // namespace atom::image -#endif // ATOM_IMAGE_TRANSFORMS_HPP +#endif // ATOM_IMAGE_TRANSFORMS_HPP diff --git a/atom/io/CMakeLists.txt b/atom/io/CMakeLists.txt index 7f348c8c..39e28e09 100644 --- a/atom/io/CMakeLists.txt +++ b/atom/io/CMakeLists.txt @@ -1,13 +1,14 @@ -# CMakeLists.txt for Atom-IO -# This project is licensed under the terms of the GPL3 license. +# CMakeLists.txt for Atom-IO This project is licensed under the terms of the +# GPL3 license. # -# Project Name: Atom-IO -# Description: IO Components for Element Astro Project -# Author: Max Qian -# License: GPL3 +# Project Name: Atom-IO Description: IO Components for Element Astro Project +# Author: Max Qian License: GPL3 cmake_minimum_required(VERSION 3.21) -project(atom-io VERSION 1.0.0 LANGUAGES C CXX) +project( + atom-io + VERSION 1.0.0 + LANGUAGES C CXX) # Include standardized module configuration include(${CMAKE_SOURCE_DIR}/cmake/ModuleDependencies.cmake) @@ -16,15 +17,12 @@ include(${CMAKE_SOURCE_DIR}/cmake/ModuleDependencies.cmake) set(SOURCES # Compression functionality compression/compress.cpp - # Filesystem operations filesystem/file_info.cpp filesystem/file_permission.cpp filesystem/pushd.cpp - # Core I/O - core/io.cpp -) + core/io.cpp) # Headers set(HEADERS @@ -38,90 +36,76 @@ set(HEADERS glob.hpp io.hpp pushd.hpp - # Actual implementation headers (in subdirectories) async/async_compress.hpp async/async_glob.hpp async/async_io.hpp - compression/compress.hpp - filesystem/file_info.hpp filesystem/file_permission.hpp filesystem/pushd.hpp - core/glob.hpp - core/io.hpp -) + core/io.hpp) # Dependencies -set(LIBS - loguru - ZLIB::ZLIB - ${CMAKE_THREAD_LIBS_INIT} -) +set(LIBS loguru ZLIB::ZLIB ${CMAKE_THREAD_LIBS_INIT}) # Try to find minizip-ng find_package(PkgConfig QUIET) if(PkgConfig_FOUND) - pkg_check_modules(MINIZIP QUIET minizip-ng) - if(MINIZIP_FOUND) - list(APPEND LIBS ${MINIZIP_LIBRARIES}) - message(STATUS "minizip-ng found via pkg-config") - endif() + pkg_check_modules(MINIZIP QUIET minizip-ng) + if(MINIZIP_FOUND) + list(APPEND LIBS ${MINIZIP_LIBRARIES}) + message(STATUS "minizip-ng found via pkg-config") + endif() endif() # Fallback: try to find minizip via find_package if(NOT MINIZIP_FOUND) - find_package(minizip-ng QUIET) - if(minizip-ng_FOUND) - list(APPEND LIBS minizip-ng::minizip-ng) - set(MINIZIP_FOUND TRUE) - message(STATUS "minizip-ng found via find_package") - endif() + find_package(minizip-ng QUIET) + if(minizip-ng_FOUND) + list(APPEND LIBS minizip-ng::minizip-ng) + set(MINIZIP_FOUND TRUE) + message(STATUS "minizip-ng found via find_package") + endif() endif() # If still not found, check if we can build without it if(NOT MINIZIP_FOUND) - message(STATUS "minizip-ng not found - compression features will be limited") - set(ATOM_IO_NO_MINIZIP TRUE) + message(STATUS "minizip-ng not found - compression features will be limited") + set(ATOM_IO_NO_MINIZIP TRUE) endif() find_package(TBB QUIET) if(TBB_FOUND) - list(APPEND LIBS TBB::tbb) - message(STATUS "TBB found and will be used for parallel algorithms") + list(APPEND LIBS TBB::tbb) + message(STATUS "TBB found and will be used for parallel algorithms") else() - message(STATUS "TBB not found - parallel algorithms will use standard library") + message( + STATUS "TBB not found - parallel algorithms will use standard library") endif() if(WIN32) - list(APPEND LIBS ws2_32 wsock32) + list(APPEND LIBS ws2_32 wsock32) endif() # Check for ASIO support and add async sources if available find_package(asio QUIET) if(asio_FOUND) - # Add async sources when ASIO is available - list(APPEND SOURCES - async/async_compress.cpp - async/async_glob.cpp - async/async_io.cpp - ) - message(STATUS "ASIO found - async I/O features enabled") + # Add async sources when ASIO is available + list(APPEND SOURCES async/async_compress.cpp async/async_glob.cpp + async/async_io.cpp) + message(STATUS "ASIO found - async I/O features enabled") else() - find_path(ASIO_INCLUDE_DIR NAMES asio.hpp) - if(ASIO_INCLUDE_DIR) - # Add async sources when ASIO is available - list(APPEND SOURCES - async/async_compress.cpp - async/async_glob.cpp - async/async_io.cpp - ) - message(STATUS "ASIO found via include path - async I/O features enabled") - else() - message(STATUS "ASIO not found - async I/O features disabled") - endif() + find_path(ASIO_INCLUDE_DIR NAMES asio.hpp) + if(ASIO_INCLUDE_DIR) + # Add async sources when ASIO is available + list(APPEND SOURCES async/async_compress.cpp async/async_glob.cpp + async/async_io.cpp) + message(STATUS "ASIO found via include path - async I/O features enabled") + else() + message(STATUS "ASIO not found - async I/O features disabled") + endif() endif() # Create library target @@ -135,19 +119,17 @@ target_link_libraries(atom-io PRIVATE ${LIBS} atom-async) # Apply ASIO configuration if(asio_FOUND) - target_compile_definitions(atom-io PUBLIC ASIO_STANDALONE ATOM_USE_ASIO) - target_link_libraries(atom-io PUBLIC asio::asio) + target_compile_definitions(atom-io PUBLIC ASIO_STANDALONE ATOM_USE_ASIO) + target_link_libraries(atom-io PUBLIC asio::asio) elseif(ASIO_INCLUDE_DIR) - target_compile_definitions(atom-io PUBLIC ASIO_STANDALONE ATOM_USE_ASIO) - target_include_directories(atom-io PUBLIC ${ASIO_INCLUDE_DIR}) + target_compile_definitions(atom-io PUBLIC ASIO_STANDALONE ATOM_USE_ASIO) + target_include_directories(atom-io PUBLIC ${ASIO_INCLUDE_DIR}) endif() # Apply minizip definitions if not found if(ATOM_IO_NO_MINIZIP) - target_compile_definitions(atom-io PRIVATE ATOM_IO_NO_MINIZIP) + target_compile_definitions(atom-io PRIVATE ATOM_IO_NO_MINIZIP) endif() # Install headers -install(FILES ${HEADERS} - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/atom/io -) +install(FILES ${HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/atom/io) diff --git a/atom/io/async/async_compress.cpp b/atom/io/async/async_compress.cpp index dc843010..644137cd 100644 --- a/atom/io/async/async_compress.cpp +++ b/atom/io/async/async_compress.cpp @@ -62,10 +62,8 @@ void BaseCompressor::notifyCompletion(const std::error_code& ec, if (completion_handler_ && completion_notified_.compare_exchange_strong(expected, true)) { auto handler = completion_handler_; - asio::post(io_context_, - [handler = std::move(handler), ec, bytes]() mutable { - handler(ec, bytes); - }); + asio::post(io_context_, [handler = std::move(handler), ec, + bytes]() mutable { handler(ec, bytes); }); } } @@ -364,10 +362,8 @@ void BaseDecompressor::notifyCompletion(const std::error_code& ec, if (completion_handler_ && completion_notified_.compare_exchange_strong(expected, true)) { auto handler = completion_handler_; - asio::post(io_context_, - [handler = std::move(handler), ec, bytes]() mutable { - handler(ec, bytes); - }); + asio::post(io_context_, [handler = std::move(handler), ec, + bytes]() mutable { handler(ec, bytes); }); } } @@ -434,7 +430,8 @@ void SingleFileDecompressor::start() { completion_notified_.store(false); if (!fs::exists(input_file_)) { spdlog::error("Input file does not exist: {}", input_file_.string()); - notifyCompletion(std::make_error_code(std::errc::no_such_file_or_directory)); + notifyCompletion( + std::make_error_code(std::errc::no_such_file_or_directory)); return; } diff --git a/atom/io/async/async_glob.cpp b/atom/io/async/async_glob.cpp index 6f6b1698..16bad4ef 100644 --- a/atom/io/async/async_glob.cpp +++ b/atom/io/async/async_glob.cpp @@ -39,7 +39,8 @@ static auto simdStringSearch(std::string_view haystack, char needle) -> size_t { // Process 32 bytes at a time for (; i + 32 <= haystack.size(); i += 32) { - __m256i chunk = _mm256_loadu_si256(reinterpret_cast(data + i)); + __m256i chunk = + _mm256_loadu_si256(reinterpret_cast(data + i)); __m256i cmp = _mm256_cmpeq_epi8(chunk, needle_vec); int mask = _mm256_movemask_epi8(cmp); @@ -67,7 +68,8 @@ static auto simdStringSearch(std::string_view haystack, char needle) -> size_t { // Process 16 bytes at a time for (; i + 16 <= haystack.size(); i += 16) { - __m128i chunk = _mm_loadu_si128(reinterpret_cast(data + i)); + __m128i chunk = + _mm_loadu_si128(reinterpret_cast(data + i)); __m128i cmp = _mm_cmpeq_epi8(chunk, needle_vec); int mask = _mm_movemask_epi8(cmp); @@ -113,7 +115,8 @@ auto AsyncGlob::escapeSpecialChars(std::string input) const -> std::string { result.reserve(input.size() * 2); // Escape backslashes - while (atom::io::stringReplace(input, std::string{"\\"}, std::string{R"(\\)"})) { + while (atom::io::stringReplace(input, std::string{"\\"}, + std::string{R"(\\)"})) { } // Escape regex special characters [&~|] @@ -135,10 +138,9 @@ auto AsyncGlob::escapeSpecialChars(std::string input) const -> std::string { * @param innerIndex End index of the character class * @return The processed character class string */ -auto AsyncGlob::processCharacterClass(std::string_view pattern, - std::size_t& index, - std::size_t innerIndex) const - -> std::string { +auto AsyncGlob::processCharacterClass( + std::string_view pattern, std::size_t& index, + std::size_t innerIndex) const -> std::string { auto stuff = std::string(pattern.substr(index, innerIndex - index)); #if ATOM_ENABLE_ABSL @@ -165,10 +167,9 @@ auto AsyncGlob::processCharacterClass(std::string_view pattern, * @param innerIndex End index of the character class * @return The processed character range string */ -auto AsyncGlob::processCharacterRanges(std::string_view pattern, - std::size_t& index, - std::size_t innerIndex) const - -> std::string { +auto AsyncGlob::processCharacterRanges( + std::string_view pattern, std::size_t& index, + std::size_t innerIndex) const -> std::string { std::vector chunks; std::size_t chunkIndex = 0; @@ -197,9 +198,11 @@ auto AsyncGlob::processCharacterRanges(std::string_view pattern, bool first = true; for (auto& chunk : chunks) { // Escape backslashes and hyphens - while (atom::io::stringReplace(chunk, std::string{"\\"}, std::string{R"(\\)"})) { + while (atom::io::stringReplace(chunk, std::string{"\\"}, + std::string{R"(\\)"})) { } - while (atom::io::stringReplace(chunk, std::string{"-"}, std::string{R"(\-)"})) { + while (atom::io::stringReplace(chunk, std::string{"-"}, + std::string{R"(\-)"})) { } if (first) { @@ -263,10 +266,9 @@ auto AsyncGlob::escapeRegexChar(char currentChar, * @param patternSize Size of the pattern * @return The processed bracket expression string */ -auto AsyncGlob::processBracketExpression(std::string_view pattern, - std::size_t& index, - std::size_t patternSize) const - -> std::string { +auto AsyncGlob::processBracketExpression( + std::string_view pattern, std::size_t& index, + std::size_t patternSize) const -> std::string { auto innerIndex = index; // Handle negation character '!' @@ -503,10 +505,12 @@ auto AsyncGlob::hasMagic(std::string_view pathname) noexcept -> bool { char c = pathname[i]; if (c == '*' || c == '?' || c == '[') { // Check if this character is escaped - if (i > 0 && pathname[i-1] == '\\') { - continue; // This magic character is escaped + if (i > 0 && pathname[i - 1] == '\\') { + continue; // This magic character is escaped } - spdlog::info("AsyncGlob::hasMagic returning: true (found unescaped '{}')", c); + spdlog::info( + "AsyncGlob::hasMagic returning: true (found unescaped '{}')", + c); return true; } } diff --git a/atom/io/async/async_glob.hpp b/atom/io/async/async_glob.hpp index 096f38a1..6eeeee7e 100644 --- a/atom/io/async/async_glob.hpp +++ b/atom/io/async/async_glob.hpp @@ -569,5 +569,3 @@ inline AsyncGlob::Task> AsyncGlob::glob_async( } // namespace atom::io #endif // ATOM_USE_ASIO - - diff --git a/atom/io/async/async_io.cpp b/atom/io/async/async_io.cpp index 759e42f1..1d89e6e5 100644 --- a/atom/io/async/async_io.cpp +++ b/atom/io/async/async_io.cpp @@ -39,8 +39,6 @@ bool AsyncFile::validatePath(std::string_view path) noexcept { return ::atom::io::path_utils::validatePath(path); } - - #ifndef ATOM_USE_ASIO template void AsyncFile::scheduleTimeout(std::chrono::milliseconds timeout, @@ -52,7 +50,8 @@ void AsyncFile::scheduleTimeout(std::chrono::milliseconds timeout, } #endif -bool AsyncFile::validatePermissions(std::string_view path, bool write_access) noexcept { +bool AsyncFile::validatePermissions(std::string_view path, + bool write_access) noexcept { if (!validatePath(path)) { return false; } @@ -68,7 +67,8 @@ bool AsyncFile::validatePermissions(std::string_view path, bool write_access) no // Check if file/directory exists if (!std::filesystem::exists(fs_path)) { - // For write operations, check if parent directory exists and is writable + // For write operations, check if parent directory exists and is + // writable if (write_access) { auto parent = fs_path.parent_path(); @@ -113,8 +113,8 @@ bool AsyncFile::validatePermissions(std::string_view path, bool write_access) no // Basic permission check (this is platform-dependent) using perms_t = std::filesystem::perms; bool readable = (perms & perms_t::owner_read) != perms_t::none || - (perms & perms_t::group_read) != perms_t::none || - (perms & perms_t::others_read) != perms_t::none; + (perms & perms_t::group_read) != perms_t::none || + (perms & perms_t::others_read) != perms_t::none; if (!readable) { return false; @@ -123,8 +123,8 @@ bool AsyncFile::validatePermissions(std::string_view path, bool write_access) no // Check write permissions if required if (write_access) { bool writable = (perms & perms_t::owner_write) != perms_t::none || - (perms & perms_t::group_write) != perms_t::none || - (perms & perms_t::others_write) != perms_t::none; + (perms & perms_t::group_write) != perms_t::none || + (perms & perms_t::others_write) != perms_t::none; return writable; } @@ -156,7 +156,8 @@ std::string AsyncFile::sanitizeFilename(std::string_view filename) noexcept { } // Trim leading/trailing spaces and dots - while (!result.empty() && (result.front() == ' ' || result.front() == '.')) { + while (!result.empty() && + (result.front() == ' ' || result.front() == '.')) { result.erase(0, 1); } while (!result.empty() && (result.back() == ' ' || result.back() == '.')) { @@ -176,16 +177,17 @@ std::string AsyncFile::sanitizeFilename(std::string_view filename) noexcept { #ifdef _WIN32 // Check for reserved names on Windows and append suffix if needed std::string upper_result = result; - std::transform(upper_result.begin(), upper_result.end(), upper_result.begin(), ::toupper); + std::transform(upper_result.begin(), upper_result.end(), + upper_result.begin(), ::toupper); const std::vector reserved_names = { - "CON", "PRN", "AUX", "NUL", - "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", - "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9" - }; + "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", + "COM5", "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3", + "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9"}; for (const auto& reserved : reserved_names) { - if (upper_result == reserved || upper_result.starts_with(reserved + ".")) { + if (upper_result == reserved || + upper_result.starts_with(reserved + ".")) { result += "_file"; break; } @@ -277,14 +279,16 @@ void AsyncFile::asyncBatchRead( /** * @brief High-performance SIMD-optimized buffer comparison * - * Uses vectorized instructions (AVX2/SSE4.2) when available for optimal performance. - * Falls back to standard library implementation on unsupported platforms. + * Uses vectorized instructions (AVX2/SSE4.2) when available for optimal + * performance. Falls back to standard library implementation on unsupported + * platforms. * * @param buffer1 First buffer to compare * @param buffer2 Second buffer to compare * @return true if buffers are identical, false otherwise */ -bool AsyncFile::simdBufferCompare(std::span buffer1, std::span buffer2) noexcept { +bool AsyncFile::simdBufferCompare(std::span buffer1, + std::span buffer2) noexcept { if (buffer1.size() != buffer2.size()) { return false; } @@ -301,8 +305,10 @@ bool AsyncFile::simdBufferCompare(std::span buffer1, std::span(data1 + i)); - __m256i chunk2 = _mm256_loadu_si256(reinterpret_cast(data2 + i)); + __m256i chunk1 = + _mm256_loadu_si256(reinterpret_cast(data1 + i)); + __m256i chunk2 = + _mm256_loadu_si256(reinterpret_cast(data2 + i)); __m256i cmp = _mm256_cmpeq_epi8(chunk1, chunk2); int mask = _mm256_movemask_epi8(cmp); if (mask != 0xFFFFFFFF) { @@ -321,8 +327,10 @@ bool AsyncFile::simdBufferCompare(std::span buffer1, std::span(data1 + i)); - __m128i chunk2 = _mm_loadu_si128(reinterpret_cast(data2 + i)); + __m128i chunk1 = + _mm_loadu_si128(reinterpret_cast(data1 + i)); + __m128i chunk2 = + _mm_loadu_si128(reinterpret_cast(data2 + i)); __m128i cmp = _mm_cmpeq_epi8(chunk1, chunk2); int mask = _mm_movemask_epi8(cmp); if (mask != 0xFFFF) { @@ -353,7 +361,8 @@ bool AsyncFile::simdBufferCompare(std::span buffer1, std::span buffer, char target) noexcept { +size_t AsyncFile::simdFindByte(std::span buffer, + char target) noexcept { if (buffer.empty()) { return std::string::npos; } @@ -367,7 +376,8 @@ size_t AsyncFile::simdFindByte(std::span buffer, char target) noexce // Process 32 bytes at a time for (; i + 32 <= size; i += 32) { - __m256i chunk = _mm256_loadu_si256(reinterpret_cast(data + i)); + __m256i chunk = + _mm256_loadu_si256(reinterpret_cast(data + i)); __m256i cmp = _mm256_cmpeq_epi8(chunk, target_vec); int mask = _mm256_movemask_epi8(cmp); @@ -389,7 +399,8 @@ size_t AsyncFile::simdFindByte(std::span buffer, char target) noexce // Process 16 bytes at a time for (; i + 16 <= size; i += 16) { - __m128i chunk = _mm_loadu_si128(reinterpret_cast(data + i)); + __m128i chunk = + _mm_loadu_si128(reinterpret_cast(data + i)); __m128i cmp = _mm_cmpeq_epi8(chunk, target_vec); int mask = _mm_movemask_epi8(cmp); @@ -408,15 +419,16 @@ size_t AsyncFile::simdFindByte(std::span buffer, char target) noexce #else // Fallback to standard library auto it = std::find(buffer.begin(), buffer.end(), target); - return it != buffer.end() ? std::distance(buffer.begin(), it) : std::string::npos; + return it != buffer.end() ? std::distance(buffer.begin(), it) + : std::string::npos; #endif } /** * @brief SIMD-optimized memory initialization * - * Efficiently sets all bytes in a buffer to a specific value using vectorized instructions. - * Processes 32 bytes at a time with AVX2 or 16 bytes with SSE4.2. + * Efficiently sets all bytes in a buffer to a specific value using vectorized + * instructions. Processes 32 bytes at a time with AVX2 or 16 bytes with SSE4.2. * * @param buffer Buffer to initialize * @param value Byte value to set @@ -461,12 +473,15 @@ void AsyncFile::simdMemorySet(std::span buffer, char value) noexcept { #endif } -void AsyncFile::asyncBatchWrite(std::span> file_data_pairs, - std::function)> callback) { +void AsyncFile::asyncBatchWrite( + std::span> file_data_pairs, + std::function)> callback) { if (file_data_pairs.empty()) { if (callback) { - auto result = AsyncResult::error_result("Empty file-data pairs list"); - executeAsync([result = std::move(result), callback = std::move(callback)]() mutable { + auto result = + AsyncResult::error_result("Empty file-data pairs list"); + executeAsync([result = std::move(result), + callback = std::move(callback)]() mutable { callback(std::move(result)); }); } @@ -480,43 +495,49 @@ void AsyncFile::asyncBatchWrite(std::span(data.data(), data.size()), - [i, total_files, completed, errors, callback](AsyncResult result) { - if (!result.success) { - (*errors)[i] = result.error_message; - } + asyncWrite( + filename, std::span(data.data(), data.size()), + [i, total_files, completed, errors, + callback](AsyncResult result) { + if (!result.success) { + (*errors)[i] = result.error_message; + } - size_t current_completed = completed->fetch_add(1) + 1; - if (current_completed == total_files) { - // All operations completed - std::string error_messages; - for (size_t j = 0; j < total_files; ++j) { - if (!(*errors)[j].empty()) { - if (!error_messages.empty()) { - error_messages += "; "; + size_t current_completed = completed->fetch_add(1) + 1; + if (current_completed == total_files) { + // All operations completed + std::string error_messages; + for (size_t j = 0; j < total_files; ++j) { + if (!(*errors)[j].empty()) { + if (!error_messages.empty()) { + error_messages += "; "; + } + error_messages += "File " + std::to_string(j) + + ": " + (*errors)[j]; } - error_messages += "File " + std::to_string(j) + ": " + (*errors)[j]; } - } - if (error_messages.empty()) { - auto final_result = AsyncResult::success_result(); - callback(std::move(final_result)); - } else { - auto final_result = AsyncResult::error_result(std::move(error_messages)); - callback(std::move(final_result)); + if (error_messages.empty()) { + auto final_result = AsyncResult::success_result(); + callback(std::move(final_result)); + } else { + auto final_result = AsyncResult::error_result( + std::move(error_messages)); + callback(std::move(final_result)); + } } - } - }); + }); } } -void AsyncFile::asyncBatchDelete(std::span files, - std::function)> callback) { +void AsyncFile::asyncBatchDelete( + std::span files, + std::function)> callback) { if (files.empty()) { if (callback) { auto result = AsyncResult::error_result("Empty file list"); - executeAsync([result = std::move(result), callback = std::move(callback)]() mutable { + executeAsync([result = std::move(result), + callback = std::move(callback)]() mutable { callback(std::move(result)); }); } @@ -528,7 +549,8 @@ void AsyncFile::asyncBatchDelete(std::span files, auto errors = std::make_shared>(total_files); for (size_t i = 0; i < total_files; ++i) { - asyncDelete(files[i], [i, total_files, completed, errors, callback](AsyncResult result) { + asyncDelete(files[i], [i, total_files, completed, errors, + callback](AsyncResult result) { if (!result.success) { (*errors)[i] = result.error_message; } @@ -542,7 +564,8 @@ void AsyncFile::asyncBatchDelete(std::span files, if (!error_messages.empty()) { error_messages += "; "; } - error_messages += "File " + std::to_string(j) + ": " + (*errors)[j]; + error_messages += + "File " + std::to_string(j) + ": " + (*errors)[j]; } } @@ -550,7 +573,8 @@ void AsyncFile::asyncBatchDelete(std::span files, auto final_result = AsyncResult::success_result(); callback(std::move(final_result)); } else { - auto final_result = AsyncResult::error_result(std::move(error_messages)); + auto final_result = AsyncResult::error_result( + std::move(error_messages)); callback(std::move(final_result)); } } @@ -585,58 +609,111 @@ template void AsyncFile::scheduleTimeout>( template std::string AsyncFile::toString(std::string&&); template std::string AsyncFile::toString(std::string_view&&); template std::string AsyncFile::toString(const char*&&); -template std::string AsyncFile::toString(std::filesystem::path&&); +template std::string AsyncFile::toString( + std::filesystem::path&&); // AsyncRead instantiations -template void AsyncFile::asyncRead(std::string&&, std::function)>); -template void AsyncFile::asyncRead(std::string_view&&, std::function)>); -template void AsyncFile::asyncRead(const char*&&, std::function)>); -template void AsyncFile::asyncRead(std::filesystem::path&&, std::function)>); +template void AsyncFile::asyncRead( + std::string&&, std::function)>); +template void AsyncFile::asyncRead( + std::string_view&&, std::function)>); +template void AsyncFile::asyncRead( + const char*&&, std::function)>); +template void AsyncFile::asyncRead( + std::filesystem::path&&, std::function)>); // AsyncWrite instantiations -template void AsyncFile::asyncWrite(std::string&&, std::span, std::function)>); -template void AsyncFile::asyncWrite(std::string_view&&, std::span, std::function)>); -template void AsyncFile::asyncWrite(const char*&&, std::span, std::function)>); -template void AsyncFile::asyncWrite(std::filesystem::path&&, std::span, std::function)>); +template void AsyncFile::asyncWrite( + std::string&&, std::span, + std::function)>); +template void AsyncFile::asyncWrite( + std::string_view&&, std::span, + std::function)>); +template void AsyncFile::asyncWrite( + const char*&&, std::span, + std::function)>); +template void AsyncFile::asyncWrite( + std::filesystem::path&&, std::span, + std::function)>); // AsyncDelete instantiations -template void AsyncFile::asyncDelete(std::string&&, std::function)>); -template void AsyncFile::asyncDelete(std::string_view&&, std::function)>); -template void AsyncFile::asyncDelete(const char*&&, std::function)>); -template void AsyncFile::asyncDelete(std::filesystem::path&&, std::function)>); +template void AsyncFile::asyncDelete( + std::string&&, std::function)>); +template void AsyncFile::asyncDelete( + std::string_view&&, std::function)>); +template void AsyncFile::asyncDelete( + const char*&&, std::function)>); +template void AsyncFile::asyncDelete( + std::filesystem::path&&, std::function)>); // AsyncStat instantiations -template void AsyncFile::asyncStat(std::string&&, std::function)>); -template void AsyncFile::asyncStat(std::string_view&&, std::function)>); -template void AsyncFile::asyncStat(const char*&&, std::function)>); -template void AsyncFile::asyncStat(std::filesystem::path&&, std::function)>); +template void AsyncFile::asyncStat( + std::string&&, + std::function)>); +template void AsyncFile::asyncStat( + std::string_view&&, + std::function)>); +template void AsyncFile::asyncStat( + const char*&&, + std::function)>); +template void AsyncFile::asyncStat( + std::filesystem::path&&, + std::function)>); // AsyncChangePermissions instantiations -template void AsyncFile::asyncChangePermissions(std::string&&, std::filesystem::perms, std::function)>); -template void AsyncFile::asyncChangePermissions(std::string_view&&, std::filesystem::perms, std::function)>); -template void AsyncFile::asyncChangePermissions(const char*&&, std::filesystem::perms, std::function)>); -template void AsyncFile::asyncChangePermissions(std::filesystem::path&&, std::filesystem::perms, std::function)>); +template void AsyncFile::asyncChangePermissions( + std::string&&, std::filesystem::perms, + std::function)>); +template void AsyncFile::asyncChangePermissions( + std::string_view&&, std::filesystem::perms, + std::function)>); +template void AsyncFile::asyncChangePermissions( + const char*&&, std::filesystem::perms, + std::function)>); +template void AsyncFile::asyncChangePermissions( + std::filesystem::path&&, std::filesystem::perms, + std::function)>); // AsyncExists instantiations -template void AsyncFile::asyncExists(std::string&&, std::function)>); -template void AsyncFile::asyncExists(std::string_view&&, std::function)>); -template void AsyncFile::asyncExists(const char*&&, std::function)>); -template void AsyncFile::asyncExists(std::filesystem::path&&, std::function)>); +template void AsyncFile::asyncExists( + std::string&&, std::function)>); +template void AsyncFile::asyncExists( + std::string_view&&, std::function)>); +template void AsyncFile::asyncExists( + const char*&&, std::function)>); +template void AsyncFile::asyncExists( + std::filesystem::path&&, std::function)>); // Directory operations instantiations -template void AsyncFile::asyncCreateDirectory(std::string&&, std::function)>); -template void AsyncFile::asyncCreateDirectory(std::string_view&&, std::function)>); -template void AsyncFile::asyncCreateDirectory(const char*&&, std::function)>); -template void AsyncFile::asyncCreateDirectory(std::filesystem::path&&, std::function)>); - -template void AsyncFile::asyncRemoveDirectory(std::string&&, std::function)>); -template void AsyncFile::asyncRemoveDirectory(std::string_view&&, std::function)>); -template void AsyncFile::asyncRemoveDirectory(const char*&&, std::function)>); -template void AsyncFile::asyncRemoveDirectory(std::filesystem::path&&, std::function)>); - -template void AsyncFile::asyncListDirectory(std::string&&, std::function>)>); -template void AsyncFile::asyncListDirectory(std::string_view&&, std::function>)>); -template void AsyncFile::asyncListDirectory(const char*&&, std::function>)>); -template void AsyncFile::asyncListDirectory(std::filesystem::path&&, std::function>)>); +template void AsyncFile::asyncCreateDirectory( + std::string&&, std::function)>); +template void AsyncFile::asyncCreateDirectory( + std::string_view&&, std::function)>); +template void AsyncFile::asyncCreateDirectory( + const char*&&, std::function)>); +template void AsyncFile::asyncCreateDirectory( + std::filesystem::path&&, std::function)>); + +template void AsyncFile::asyncRemoveDirectory( + std::string&&, std::function)>); +template void AsyncFile::asyncRemoveDirectory( + std::string_view&&, std::function)>); +template void AsyncFile::asyncRemoveDirectory( + const char*&&, std::function)>); +template void AsyncFile::asyncRemoveDirectory( + std::filesystem::path&&, std::function)>); + +template void AsyncFile::asyncListDirectory( + std::string&&, + std::function>)>); +template void AsyncFile::asyncListDirectory( + std::string_view&&, + std::function>)>); +template void AsyncFile::asyncListDirectory( + const char*&&, + std::function>)>); +template void AsyncFile::asyncListDirectory( + std::filesystem::path&&, + std::function>)>); } // namespace atom::async::io diff --git a/atom/io/async/async_io.hpp b/atom/io/async/async_io.hpp index 3bd69ace..0016a744 100644 --- a/atom/io/async/async_io.hpp +++ b/atom/io/async/async_io.hpp @@ -49,10 +49,11 @@ concept PathString = std::convertible_to || * @brief Concept for types that can be used as file content */ template -concept FileContent = std::ranges::contiguous_range && - (std::same_as, char> || - std::same_as, unsigned char> || - std::same_as, std::byte>); +concept FileContent = + std::ranges::contiguous_range && + (std::same_as, char> || + std::same_as, unsigned char> || + std::same_as, std::byte>); /** * @brief Concept for callback functions that can handle AsyncResult @@ -64,7 +65,7 @@ concept FileContent = std::ranges::contiguous_range && */ template concept FilePermissions = std::same_as || - std::convertible_to; + std::convertible_to; /** * @brief Concept for types that represent file sizes @@ -76,8 +77,9 @@ concept FileSizeType = std::integral && std::unsigned_integral; * @brief Concept for types that can be used as buffer data */ template -concept BufferData = std::ranges::contiguous_range && - std::is_trivially_copyable_v>; +concept BufferData = + std::ranges::contiguous_range && + std::is_trivially_copyable_v>; /** * @brief Concept for types that can be used as compression options @@ -346,7 +348,8 @@ class AsyncFile { * @param path Path of the directory to list * @return Task that completes with directory contents */ - [[nodiscard]] atom::async::Task>> + [[nodiscard]] atom::async::Task< + AsyncResult>> listDirectory(PathString auto&& path); /** @@ -362,8 +365,8 @@ class AsyncFile { * @param filename Path to the file * @return Task that completes with file status */ - [[nodiscard]] atom::async::Task> getFileStatus( - PathString auto&& filename); + [[nodiscard]] atom::async::Task> + getFileStatus(PathString auto&& filename); /** * @brief Coroutine-based file existence check @@ -422,7 +425,8 @@ class AsyncFile { * @param write_access Whether write access is required * @return True if permissions are valid, false otherwise */ - static bool validatePermissions(std::string_view path, bool write_access = false) noexcept; + static bool validatePermissions(std::string_view path, + bool write_access = false) noexcept; /** * @brief Sanitizes a filename by removing or replacing invalid characters @@ -434,20 +438,24 @@ class AsyncFile { /** * @brief SIMD-optimized buffer operations for high-performance I/O */ - static bool simdBufferCompare(std::span buffer1, std::span buffer2) noexcept; - static size_t simdFindByte(std::span buffer, char target) noexcept; + static bool simdBufferCompare(std::span buffer1, + std::span buffer2) noexcept; + static size_t simdFindByte(std::span buffer, + char target) noexcept; static void simdMemorySet(std::span buffer, char value) noexcept; /** * @brief Performance optimization functions */ /** - * @brief Asynchronously writes multiple files in parallel for optimal performance + * @brief Asynchronously writes multiple files in parallel for optimal + * performance * @param file_data_pairs Pairs of filename and data to write * @param callback Callback function for the batch operation result */ - void asyncBatchWrite(std::span> file_data_pairs, - std::function)> callback); + void asyncBatchWrite( + std::span> file_data_pairs, + std::function)> callback); /** * @brief Asynchronously deletes multiple files in parallel @@ -455,21 +463,24 @@ class AsyncFile { * @param callback Callback function for the batch operation result */ void asyncBatchDelete(std::span files, - std::function)> callback); + std::function)> callback); /** - * @brief Asynchronously reads a file in chunks for memory-efficient processing + * @brief Asynchronously reads a file in chunks for memory-efficient + * processing * @param filename Path to the file to read * @param chunk_size Size of each chunk to read * @param chunk_callback Callback for each chunk read * @param completion_callback Callback when reading is complete */ template - void asyncStreamRead(T&& filename, size_t chunk_size, - std::function)> chunk_callback, - std::function)> completion_callback); + void asyncStreamRead( + T&& filename, size_t chunk_size, + std::function)> chunk_callback, + std::function)> completion_callback); /** - * @brief Asynchronously writes data to a file in chunks for memory efficiency + * @brief Asynchronously writes data to a file in chunks for memory + * efficiency * @param filename Path to the file to write * @param data Data to write * @param chunk_size Size of each chunk to write @@ -477,7 +488,8 @@ class AsyncFile { */ template void asyncStreamWrite(T&& filename, std::span data, - size_t chunk_size, std::function)> callback); + size_t chunk_size, + std::function)> callback); /** * @brief Converts path-like types to string efficiently @@ -503,8 +515,8 @@ class AsyncFile { * @brief Legacy AsyncDirectory interface for backward compatibility * @deprecated Use AsyncFile methods instead for unified interface */ -class [[deprecated("Use AsyncFile for unified file/directory operations")]] -AsyncDirectory { +class [[deprecated( + "Use AsyncFile for unified file/directory operations")]] AsyncDirectory { public: #ifdef ATOM_USE_ASIO explicit AsyncDirectory(asio::io_context& io_context) noexcept; @@ -560,13 +572,15 @@ void AsyncFile::executeAsync(F&& operation) { } template -void AsyncFile::asyncRead(T&& filename, - std::function)> callback) { - executeAsync([filename = toString(std::forward(filename)), callback = std::move(callback)]() { +void AsyncFile::asyncRead( + T&& filename, std::function)> callback) { + executeAsync([filename = toString(std::forward(filename)), + callback = std::move(callback)]() { try { std::ifstream file(filename, std::ios::binary); if (!file) { - callback(AsyncResult::error_result("Failed to open file: " + filename)); + callback(AsyncResult::error_result( + "Failed to open file: " + filename)); return; } @@ -576,11 +590,13 @@ void AsyncFile::asyncRead(T&& filename, std::string content(size, '\0'); if (!file.read(content.data(), size)) { - callback(AsyncResult::error_result("Failed to read file: " + filename)); + callback(AsyncResult::error_result( + "Failed to read file: " + filename)); return; } - callback(AsyncResult::success_result(std::move(content))); + callback( + AsyncResult::success_result(std::move(content))); } catch (const std::exception& e) { callback(AsyncResult::error_result(e.what())); } @@ -589,19 +605,21 @@ void AsyncFile::asyncRead(T&& filename, template void AsyncFile::asyncWrite(T&& filename, std::span content, - std::function)> callback) { + std::function)> callback) { executeAsync([filename = toString(std::forward(filename)), content = std::string(content.begin(), content.end()), callback = std::move(callback)]() { try { std::ofstream file(filename, std::ios::binary); if (!file) { - callback(AsyncResult::error_result("Failed to open file for writing: " + filename)); + callback(AsyncResult::error_result( + "Failed to open file for writing: " + filename)); return; } if (!file.write(content.data(), content.size())) { - callback(AsyncResult::error_result("Failed to write to file: " + filename)); + callback(AsyncResult::error_result( + "Failed to write to file: " + filename)); return; } @@ -614,13 +632,15 @@ void AsyncFile::asyncWrite(T&& filename, std::span content, template void AsyncFile::asyncDelete(T&& filename, - std::function)> callback) { - executeAsync([filename = toString(std::forward(filename)), callback = std::move(callback)]() { + std::function)> callback) { + executeAsync([filename = toString(std::forward(filename)), + callback = std::move(callback)]() { try { if (std::filesystem::remove(filename)) { callback(AsyncResult::success_result()); } else { - callback(AsyncResult::error_result("Failed to delete file: " + filename)); + callback(AsyncResult::error_result( + "Failed to delete file: " + filename)); } } catch (const std::exception& e) { callback(AsyncResult::error_result(e.what())); @@ -629,34 +649,43 @@ void AsyncFile::asyncDelete(T&& filename, } template -void AsyncFile::asyncStat(T&& filename, - std::function)> callback) { - executeAsync([filename = toString(std::forward(filename)), callback = std::move(callback)]() { +void AsyncFile::asyncStat( + T&& filename, + std::function)> callback) { + executeAsync([filename = toString(std::forward(filename)), + callback = std::move(callback)]() { try { std::error_code ec; auto status = std::filesystem::status(filename, ec); if (ec) { - callback(AsyncResult::error_result( - "Failed to get file status: " + filename + " - " + ec.message())); + callback( + AsyncResult::error_result( + "Failed to get file status: " + filename + " - " + + ec.message())); return; } - callback(AsyncResult::success_result(std::move(status))); + callback(AsyncResult::success_result( + std::move(status))); } catch (const std::exception& e) { - callback(AsyncResult::error_result(e.what())); + callback(AsyncResult::error_result( + e.what())); } }); } template -void AsyncFile::asyncChangePermissions(T&& filename, std::filesystem::perms perms, - std::function)> callback) { - executeAsync([filename = toString(std::forward(filename)), perms, callback = std::move(callback)]() { +void AsyncFile::asyncChangePermissions( + T&& filename, std::filesystem::perms perms, + std::function)> callback) { + executeAsync([filename = toString(std::forward(filename)), perms, + callback = std::move(callback)]() { try { std::error_code ec; std::filesystem::permissions(filename, perms, ec); if (ec) { callback(AsyncResult::error_result( - "Failed to change permissions: " + filename + " - " + ec.message())); + "Failed to change permissions: " + filename + " - " + + ec.message())); return; } callback(AsyncResult::success_result()); @@ -668,8 +697,9 @@ void AsyncFile::asyncChangePermissions(T&& filename, std::filesystem::perms perm template void AsyncFile::asyncExists(T&& filename, - std::function)> callback) { - executeAsync([filename = toString(std::forward(filename)), callback = std::move(callback)]() { + std::function)> callback) { + executeAsync([filename = toString(std::forward(filename)), + callback = std::move(callback)]() { try { bool exists = std::filesystem::exists(filename); callback(AsyncResult::success_result(std::move(exists))); @@ -680,15 +710,17 @@ void AsyncFile::asyncExists(T&& filename, } template -void AsyncFile::asyncCreateDirectory(T&& path, - std::function)> callback) { - executeAsync([path = toString(std::forward(path)), callback = std::move(callback)]() { +void AsyncFile::asyncCreateDirectory( + T&& path, std::function)> callback) { + executeAsync([path = toString(std::forward(path)), + callback = std::move(callback)]() { try { std::error_code ec; std::filesystem::create_directories(path, ec); if (ec) { callback(AsyncResult::error_result( - "Failed to create directory: " + path + " - " + ec.message())); + "Failed to create directory: " + path + " - " + + ec.message())); return; } callback(AsyncResult::success_result()); @@ -699,15 +731,17 @@ void AsyncFile::asyncCreateDirectory(T&& path, } template -void AsyncFile::asyncRemoveDirectory(T&& path, - std::function)> callback) { - executeAsync([path = toString(std::forward(path)), callback = std::move(callback)]() { +void AsyncFile::asyncRemoveDirectory( + T&& path, std::function)> callback) { + executeAsync([path = toString(std::forward(path)), + callback = std::move(callback)]() { try { std::error_code ec; std::filesystem::remove_all(path, ec); if (ec) { callback(AsyncResult::error_result( - "Failed to remove directory: " + path + " - " + ec.message())); + "Failed to remove directory: " + path + " - " + + ec.message())); return; } callback(AsyncResult::success_result()); @@ -718,43 +752,54 @@ void AsyncFile::asyncRemoveDirectory(T&& path, } template -void AsyncFile::asyncListDirectory(T&& path, - std::function>)> callback) { - executeAsync([path = toString(std::forward(path)), callback = std::move(callback)]() { +void AsyncFile::asyncListDirectory( + T&& path, + std::function>)> + callback) { + executeAsync([path = toString(std::forward(path)), + callback = std::move(callback)]() { try { std::vector entries; std::error_code ec; - for (const auto& entry : std::filesystem::directory_iterator(path, ec)) { + for (const auto& entry : + std::filesystem::directory_iterator(path, ec)) { if (ec) { - callback(AsyncResult>::error_result( - "Failed to iterate directory: " + path + " - " + ec.message())); + callback(AsyncResult>:: + error_result("Failed to iterate directory: " + + path + " - " + ec.message())); return; } entries.push_back(entry.path()); } - callback(AsyncResult>::success_result(std::move(entries))); + callback( + AsyncResult>::success_result( + std::move(entries))); } catch (const std::exception& e) { - callback(AsyncResult>::error_result(e.what())); + callback( + AsyncResult>::error_result( + e.what())); } }); } template void AsyncFile::asyncCopy(T&& src, U&& dest, - std::function)> callback) { + std::function)> callback) { executeAsync([src = toString(std::forward(src)), dest = toString(std::forward(dest)), callback = std::move(callback)]() { try { std::error_code ec; - std::filesystem::copy_file(src, dest, - std::filesystem::copy_options::overwrite_existing, ec); + std::filesystem::copy_file( + src, dest, std::filesystem::copy_options::overwrite_existing, + ec); if (ec) { callback(AsyncResult::error_result( - "Failed to copy file from " + src + " to " + dest + ": " + ec.message())); + "Failed to copy file from " + src + " to " + dest + ": " + + ec.message())); return; } @@ -767,7 +812,7 @@ void AsyncFile::asyncCopy(T&& src, U&& dest, template void AsyncFile::asyncMove(T&& src, U&& dest, - std::function)> callback) { + std::function)> callback) { executeAsync([src = toString(std::forward(src)), dest = toString(std::forward(dest)), callback = std::move(callback)]() { @@ -777,7 +822,8 @@ void AsyncFile::asyncMove(T&& src, U&& dest, if (ec) { callback(AsyncResult::error_result( - "Failed to move file from " + src + " to " + dest + ": " + ec.message())); + "Failed to move file from " + src + " to " + dest + ": " + + ec.message())); return; } @@ -789,37 +835,44 @@ void AsyncFile::asyncMove(T&& src, U&& dest, } template -void AsyncFile::asyncReadWithTimeout(T&& filename, std::chrono::milliseconds timeout, - std::function)> callback) { +void AsyncFile::asyncReadWithTimeout( + T&& filename, std::chrono::milliseconds timeout, + std::function)> callback) { auto filename_str = toString(std::forward(filename)); // Launch the read operation asynchronously - auto read_future = std::async(std::launch::async, [filename_str]() -> AsyncResult { - try { - std::ifstream file(filename_str, std::ios::binary); - if (!file) { - return AsyncResult::error_result("Failed to open file: " + filename_str); - } + auto read_future = std::async( + std::launch::async, [filename_str]() -> AsyncResult { + try { + std::ifstream file(filename_str, std::ios::binary); + if (!file) { + return AsyncResult::error_result( + "Failed to open file: " + filename_str); + } - file.seekg(0, std::ios::end); - auto size = file.tellg(); - file.seekg(0, std::ios::beg); + file.seekg(0, std::ios::end); + auto size = file.tellg(); + file.seekg(0, std::ios::beg); - std::string content(size, '\0'); - if (!file.read(content.data(), size)) { - return AsyncResult::error_result("Failed to read file: " + filename_str); - } + std::string content(size, '\0'); + if (!file.read(content.data(), size)) { + return AsyncResult::error_result( + "Failed to read file: " + filename_str); + } - return AsyncResult::success_result(std::move(content)); - } catch (const std::exception& e) { - return AsyncResult::error_result(e.what()); - } - }); + return AsyncResult::success_result( + std::move(content)); + } catch (const std::exception& e) { + return AsyncResult::error_result(e.what()); + } + }); // Wait for completion with timeout - executeAsync([read_future = std::move(read_future), timeout, callback = std::move(callback)]() mutable { + executeAsync([read_future = std::move(read_future), timeout, + callback = std::move(callback)]() mutable { if (read_future.wait_for(timeout) == std::future_status::timeout) { - callback(AsyncResult::error_result("Read operation timed out")); + callback(AsyncResult::error_result( + "Read operation timed out")); } else { callback(read_future.get()); } @@ -827,109 +880,128 @@ void AsyncFile::asyncReadWithTimeout(T&& filename, std::chrono::milliseconds tim } template -[[nodiscard]] atom::async::Task> AsyncFile::readFile(T&& filename) { +[[nodiscard]] atom::async::Task> AsyncFile::readFile( + T&& filename) { std::promise> promise; auto future = promise.get_future(); - asyncRead(std::forward(filename), [&promise](AsyncResult result) { - promise.set_value(std::move(result)); - }); + asyncRead(std::forward(filename), + [&promise](AsyncResult result) { + promise.set_value(std::move(result)); + }); co_return future.get(); } template -[[nodiscard]] atom::async::Task> AsyncFile::writeFile(T&& filename, std::span content) { +[[nodiscard]] atom::async::Task> AsyncFile::writeFile( + T&& filename, std::span content) { std::promise> promise; auto future = promise.get_future(); - asyncWrite(std::forward(filename), content, [&promise](AsyncResult result) { - promise.set_value(std::move(result)); - }); + asyncWrite(std::forward(filename), content, + [&promise](AsyncResult result) { + promise.set_value(std::move(result)); + }); co_return future.get(); } template -[[nodiscard]] atom::async::Task>> AsyncFile::listDirectory(T&& path) { +[[nodiscard]] atom::async::Task>> +AsyncFile::listDirectory(T&& path) { std::promise>> promise; auto future = promise.get_future(); - asyncListDirectory(std::forward(path), [&promise](AsyncResult> result) { - promise.set_value(std::move(result)); - }); + asyncListDirectory( + std::forward(path), + [&promise](AsyncResult> result) { + promise.set_value(std::move(result)); + }); co_return future.get(); } template -[[nodiscard]] atom::async::Task> AsyncFile::deleteFile(T&& filename) { +[[nodiscard]] atom::async::Task> AsyncFile::deleteFile( + T&& filename) { std::promise> promise; auto future = promise.get_future(); - asyncDelete(std::forward(filename), [&promise](AsyncResult result) { - promise.set_value(std::move(result)); - }); + asyncDelete(std::forward(filename), + [&promise](AsyncResult result) { + promise.set_value(std::move(result)); + }); co_return future.get(); } template -[[nodiscard]] atom::async::Task> AsyncFile::getFileStatus(T&& filename) { +[[nodiscard]] atom::async::Task> +AsyncFile::getFileStatus(T&& filename) { std::promise> promise; auto future = promise.get_future(); - asyncStat(std::forward(filename), [&promise](AsyncResult result) { - promise.set_value(std::move(result)); - }); + asyncStat(std::forward(filename), + [&promise](AsyncResult result) { + promise.set_value(std::move(result)); + }); co_return future.get(); } template -[[nodiscard]] atom::async::Task> AsyncFile::fileExists(T&& filename) { +[[nodiscard]] atom::async::Task> AsyncFile::fileExists( + T&& filename) { std::promise> promise; auto future = promise.get_future(); - asyncExists(std::forward(filename), [&promise](AsyncResult result) { - promise.set_value(std::move(result)); - }); + asyncExists(std::forward(filename), + [&promise](AsyncResult result) { + promise.set_value(std::move(result)); + }); co_return future.get(); } template -[[nodiscard]] atom::async::Task> AsyncFile::changePermissions(T&& filename, std::filesystem::perms perms) { +[[nodiscard]] atom::async::Task> AsyncFile::changePermissions( + T&& filename, std::filesystem::perms perms) { std::promise> promise; auto future = promise.get_future(); - asyncChangePermissions(std::forward(filename), perms, [&promise](AsyncResult result) { - promise.set_value(std::move(result)); - }); + asyncChangePermissions(std::forward(filename), perms, + [&promise](AsyncResult result) { + promise.set_value(std::move(result)); + }); co_return future.get(); } template -[[nodiscard]] atom::async::Task> AsyncFile::createDirectory(T&& path) { +[[nodiscard]] atom::async::Task> AsyncFile::createDirectory( + T&& path) { std::promise> promise; auto future = promise.get_future(); - asyncCreateDirectory(std::forward(path), [&promise](AsyncResult result) { - promise.set_value(std::move(result)); - }); + asyncCreateDirectory(std::forward(path), + [&promise](AsyncResult result) { + promise.set_value(std::move(result)); + }); co_return future.get(); } template -[[nodiscard]] atom::async::Task> AsyncFile::removeDirectory(T&& path) { +[[nodiscard]] atom::async::Task> AsyncFile::removeDirectory( + T&& path) { std::promise> promise; auto future = promise.get_future(); - asyncRemoveDirectory(std::forward(path), [&promise](AsyncResult result) { - promise.set_value(std::move(result)); - }); + asyncRemoveDirectory(std::forward(path), + [&promise](AsyncResult result) { + promise.set_value(std::move(result)); + }); co_return future.get(); } @@ -1031,23 +1103,27 @@ class [[nodiscard]] Task { // Implementation of streaming functions template -void AsyncFile::asyncStreamRead(T&& filename, size_t chunk_size, - std::function)> chunk_callback, - std::function)> completion_callback) { +void AsyncFile::asyncStreamRead( + T&& filename, size_t chunk_size, + std::function)> chunk_callback, + std::function)> completion_callback) { executeAsync([filename = toString(std::forward(filename)), chunk_size, chunk_callback = std::move(chunk_callback), - completion_callback = std::move(completion_callback)]() mutable { + completion_callback = + std::move(completion_callback)]() mutable { try { std::ifstream file(filename, std::ios::binary); if (!file) { - completion_callback(AsyncResult::error_result("Failed to open file: " + filename)); + completion_callback(AsyncResult::error_result( + "Failed to open file: " + filename)); return; } std::string chunk(chunk_size, '\0'); while (file.read(chunk.data(), chunk_size) || file.gcount() > 0) { chunk.resize(file.gcount()); - chunk_callback(AsyncResult::success_result(std::string(chunk))); + chunk_callback(AsyncResult::success_result( + std::string(chunk))); chunk.resize(chunk_size); } @@ -1059,15 +1135,17 @@ void AsyncFile::asyncStreamRead(T&& filename, size_t chunk_size, } template -void AsyncFile::asyncStreamWrite(T&& filename, std::span data, - size_t chunk_size, std::function)> callback) { +void AsyncFile::asyncStreamWrite( + T&& filename, std::span data, size_t chunk_size, + std::function)> callback) { executeAsync([filename = toString(std::forward(filename)), data = std::string(data.begin(), data.end()), chunk_size, callback = std::move(callback)]() mutable { try { std::ofstream file(filename, std::ios::binary); if (!file) { - callback(AsyncResult::error_result("Failed to open file for writing: " + filename)); + callback(AsyncResult::error_result( + "Failed to open file for writing: " + filename)); return; } @@ -1075,7 +1153,8 @@ void AsyncFile::asyncStreamWrite(T&& filename, std::span data, while (written < data.size()) { size_t to_write = std::min(chunk_size, data.size() - written); if (!file.write(data.data() + written, to_write)) { - callback(AsyncResult::error_result("Failed to write chunk to file: " + filename)); + callback(AsyncResult::error_result( + "Failed to write chunk to file: " + filename)); return; } written += to_write; diff --git a/atom/io/compression/compress.cpp b/atom/io/compression/compress.cpp index 01b165c7..9ce2321e 100644 --- a/atom/io/compression/compress.cpp +++ b/atom/io/compression/compress.cpp @@ -204,7 +204,8 @@ CompressionResult compressFile(std::string_view file_path_sv, // Edge case: Check if input path is valid std::error_code ec; if (!fs::exists(input_path, ec) || ec) { - result.error_message = "Input file does not exist or is inaccessible"; + result.error_message = + "Input file does not exist or is inaccessible"; return result; } @@ -214,7 +215,8 @@ CompressionResult compressFile(std::string_view file_path_sv, return result; } - // Edge case: Check file size (avoid compressing empty files or extremely large files) + // Edge case: Check file size (avoid compressing empty files or + // extremely large files) auto file_size = fs::file_size(input_path, ec); if (ec) { result.error_message = "Cannot determine input file size"; @@ -226,11 +228,14 @@ CompressionResult compressFile(std::string_view file_path_sv, return result; } - // Edge case: Check for extremely large files (> 4GB might cause issues with some zip implementations) - constexpr auto MAX_FILE_SIZE = static_cast(4ULL * 1024 * 1024 * 1024); // 4GB + // Edge case: Check for extremely large files (> 4GB might cause issues + // with some zip implementations) + constexpr auto MAX_FILE_SIZE = + static_cast(4ULL * 1024 * 1024 * 1024); // 4GB if (file_size > MAX_FILE_SIZE) { - spdlog::warn("Compressing very large file ({}GB), this may take a long time", - file_size / (1024.0 * 1024.0 * 1024.0)); + spdlog::warn( + "Compressing very large file ({}GB), this may take a long time", + file_size / (1024.0 * 1024.0 * 1024.0)); } fs::path output_dir(output_folder_sv); @@ -243,13 +248,15 @@ CompressionResult compressFile(std::string_view file_path_sv, if (!fs::exists(output_dir, ec)) { if (!fs::create_directories(output_dir, ec) || ec) { - result.error_message = "Failed to create output directory: " + ec.message(); + result.error_message = + "Failed to create output directory: " + ec.message(); return result; } } else { // Edge case: Check if output path is actually a directory if (!fs::is_directory(output_dir, ec) || ec) { - result.error_message = "Output path exists but is not a directory"; + result.error_message = + "Output path exists but is not a directory"; return result; } } @@ -634,7 +641,7 @@ CompressionResult compressFolder(std::string_view folder_path_sv, return result; } -#endif // ATOM_IO_NO_MINIZIP +#endif // ATOM_IO_NO_MINIZIP #ifndef ATOM_IO_NO_MINIZIP CompressionResult extractZip(std::string_view zip_path_sv, @@ -1349,7 +1356,7 @@ std::optional getZipSize(std::string_view zip_path_sv) { return std::nullopt; } } -#endif // ATOM_IO_NO_MINIZIP +#endif // ATOM_IO_NO_MINIZIP // compressFileInSlices needs careful handling of filenames and manifest CompressionResult compressFileInSlices(std::string_view file_path_sv, @@ -2454,14 +2461,16 @@ decompressData>(const std::span&, size_t, #ifdef ATOM_IO_NO_MINIZIP // Stub implementations when minizip-ng is not available -CompressionResult extractZip(std::string_view, std::string_view, const DecompressionOptions&) { +CompressionResult extractZip(std::string_view, std::string_view, + const DecompressionOptions&) { CompressionResult result; result.success = false; result.error_message = "ZIP support not available - minizip-ng not found"; return result; } -CompressionResult createZip(std::string_view, std::string_view, const CompressionOptions&) { +CompressionResult createZip(std::string_view, std::string_view, + const CompressionOptions&) { CompressionResult result; result.success = false; result.error_message = "ZIP support not available - minizip-ng not found"; @@ -2479,13 +2488,9 @@ Vector listZipContents(std::string_view) { return Vector{}; } -bool fileExistsInZip(std::string_view, std::string_view) { - return false; -} +bool fileExistsInZip(std::string_view, std::string_view) { return false; } -std::optional getZipSize(std::string_view) { - return std::nullopt; -} +std::optional getZipSize(std::string_view) { return std::nullopt; } #endif } // namespace atom::io diff --git a/atom/io/compression/compress.hpp b/atom/io/compression/compress.hpp index e11a9ddd..31a9ad46 100644 --- a/atom/io/compression/compress.hpp +++ b/atom/io/compression/compress.hpp @@ -30,8 +30,9 @@ namespace atom::io { * @brief Concept for types that can be compressed */ template -concept CompressibleData = std::ranges::contiguous_range && - std::is_trivially_copyable_v>; +concept CompressibleData = + std::ranges::contiguous_range && + std::is_trivially_copyable_v>; /** * @brief Concept for compression level values diff --git a/atom/io/core/io.cpp b/atom/io/core/io.cpp index 8faf341f..6f7c1ca2 100644 --- a/atom/io/core/io.cpp +++ b/atom/io/core/io.cpp @@ -204,28 +204,44 @@ auto getExecutableNameFromPath(std::string_view path) -> std::string { template bool isFolderExists(const std::string& folderPath); template bool isFileExists(const std::string& filePath); template std::uintmax_t fileSize(const std::string& path); -template void splitFile(const std::string& filePath, std::size_t chunkSize, const std::string& outputPattern); -template void mergeFiles(const std::string& outputFilePath, std::span partFiles); +template void splitFile( + const std::string& filePath, std::size_t chunkSize, + const std::string& outputPattern); +template void mergeFiles(const std::string& outputFilePath, + std::span partFiles); template bool createDirectory(const std::string& path); template bool removeDirectory(const std::string& path); -template bool copyFile(const std::string& src_path, const std::string& dst_path); -template bool moveFile(const std::string& src_path, const std::string& dst_path); -template bool renameFile(const std::string& old_path, const std::string& new_path); +template bool copyFile(const std::string& src_path, + const std::string& dst_path); +template bool moveFile(const std::string& src_path, + const std::string& dst_path); +template bool renameFile(const std::string& old_path, + const std::string& new_path); template bool removeFile(const std::string& path); -template bool createSymlink(const std::string& target_path, const std::string& symlink_path); +template bool createSymlink( + const std::string& target_path, const std::string& symlink_path); template bool removeSymlink(const std::string& path); template std::string jwalk(const std::string& root); -template void fwalk(const std::string& root, const std::function& callback); +template void fwalk( + const std::string& root, + const std::function& callback); template bool isFolderEmpty(const std::string& folderPath); template bool isAbsolutePath(const std::string& path); -template bool changeWorkingDirectory(const std::string& directoryPath); -template std::pair getFileTimes(const std::string& filePath); -template bool isExecutableFile(const std::string& fileName, const std::string& fileExt); +template bool changeWorkingDirectory( + const std::string& directoryPath); +template std::pair getFileTimes( + const std::string& filePath); +template bool isExecutableFile( + const std::string& fileName, const std::string& fileExt); template std::size_t getFileSize(const std::string& filePath); -template bool truncateFile(const std::string& path, std::streamsize size); +template bool truncateFile(const std::string& path, + std::streamsize size); template auto checkPathType(const std::string& path) -> PathType; -template auto countLinesInFile(const std::string& filePath) -> std::optional; -template auto searchExecutableFiles(const std::string& dir, std::string_view searchStr) -> std::vector; +template auto countLinesInFile(const std::string& filePath) + -> std::optional; +template auto searchExecutableFiles(const std::string& dir, + std::string_view searchStr) + -> std::vector; // Instantiations for const char* template bool isFolderExists(const char* const& folderPath); diff --git a/atom/io/core/io.hpp b/atom/io/core/io.hpp index 6212dc4b..9f5a52aa 100644 --- a/atom/io/core/io.hpp +++ b/atom/io/core/io.hpp @@ -83,10 +83,9 @@ enum class PathType { NOT_EXISTS, REGULAR_FILE, DIRECTORY, SYMLINK, OTHER }; * @return True if the operation was successful, false otherwise. */ template -auto createDirectoriesRecursive(const P& basePath, - const std::vector& subdirs, - const CreateDirectoriesOptions& options = {}) - -> bool; +auto createDirectoriesRecursive( + const P& basePath, const std::vector& subdirs, + const CreateDirectoriesOptions& options = {}) -> bool; /** * @brief Creates a directory with date-based path under root directory. @@ -127,8 +126,8 @@ template * @return True if the operation was successful, false otherwise. */ template -[[nodiscard]] auto renameDirectory(const P1& old_path, const P2& new_path) - -> bool; +[[nodiscard]] auto renameDirectory(const P1& old_path, + const P2& new_path) -> bool; /** * @brief Moves a directory from one path to another. @@ -138,8 +137,8 @@ template * @return True if the operation was successful, false otherwise. */ template -[[nodiscard]] auto moveDirectory(const P1& old_path, const P2& new_path) - -> bool; +[[nodiscard]] auto moveDirectory(const P1& old_path, + const P2& new_path) -> bool; /** * @brief Copies a file from source path to destination path. @@ -188,8 +187,8 @@ template * @return True if the operation was successful, false otherwise. */ template -[[nodiscard]] auto createSymlink(const P1& target_path, const P2& symlink_path) - -> bool; +[[nodiscard]] auto createSymlink(const P1& target_path, + const P2& symlink_path) -> bool; /** * @brief Removes a symbolic link with the specified path. @@ -366,10 +365,9 @@ enum class FileOption { PATH, NAME }; * @remark The file type is checked by the file extension. */ template -[[nodiscard]] auto checkFileTypeInFolder(const P& folderPath, - std::span fileTypes, - FileOption fileOption) - -> std::vector; +[[nodiscard]] auto checkFileTypeInFolder( + const P& folderPath, std::span fileTypes, + FileOption fileOption) -> std::vector; /** * @brief Check whether the specified file exists and is executable. @@ -478,8 +476,8 @@ auto countLinesInFile(const P& filePath) -> std::optional; * @return std::vector Paths to found executable files */ template -auto searchExecutableFiles(const P& dir, std::string_view searchStr) - -> std::vector; +auto searchExecutableFiles(const P& dir, + std::string_view searchStr) -> std::vector; /** * @brief Classify files in a directory by extension @@ -526,10 +524,9 @@ template } template -auto createDirectoriesRecursive(const P& basePath, - const std::vector& subdirs, - const CreateDirectoriesOptions& options) - -> bool { +auto createDirectoriesRecursive( + const P& basePath, const std::vector& subdirs, + const CreateDirectoriesOptions& options) -> bool { spdlog::info("createDirectoriesRecursive called with basePath: {}", fs::path(basePath).string()); @@ -712,16 +709,16 @@ template } template -[[nodiscard]] auto renameDirectory(const P1& old_path, const P2& new_path) - -> bool { +[[nodiscard]] auto renameDirectory(const P1& old_path, + const P2& new_path) -> bool { spdlog::info("renameDirectory called with old_path: {}, new_path: {}", fs::path(old_path).string(), fs::path(new_path).string()); return moveDirectory(old_path, new_path); } template -[[nodiscard]] auto moveDirectory(const P1& old_path, const P2& new_path) - -> bool { +[[nodiscard]] auto moveDirectory(const P1& old_path, + const P2& new_path) -> bool { spdlog::info("moveDirectory called with old_path: {}, new_path: {}", fs::path(old_path).string(), fs::path(new_path).string()); @@ -932,8 +929,8 @@ template } template -[[nodiscard]] auto createSymlink(const P1& target_path, const P2& symlink_path) - -> bool { +[[nodiscard]] auto createSymlink(const P1& target_path, + const P2& symlink_path) -> bool { spdlog::info("createSymlink called with target_path: {}, symlink_path: {}", fs::path(target_path).string(), fs::path(symlink_path).string()); @@ -1093,8 +1090,8 @@ inline void walk(const fs::path& root, bool recursive, } // Helper function to build JSON structure -inline auto buildJsonStructure(const fs::path& root, bool recursive) - -> nlohmann::json { +inline auto buildJsonStructure(const fs::path& root, + bool recursive) -> nlohmann::json { spdlog::info("buildJsonStructure called with root: {}, recursive: {}", root.string(), recursive); @@ -1327,11 +1324,11 @@ template if (FileTimeToSystemTime(&creationTime, &sysTime)) { std::array buffer{}; - int written = snprintf( - buffer.data(), buffer.size(), - "%04d-%02d-%02d %02d:%02d:%02d", sysTime.wYear, - sysTime.wMonth, sysTime.wDay, sysTime.wHour, - sysTime.wMinute, sysTime.wSecond); + int written = + snprintf(buffer.data(), buffer.size(), + "%04d-%02d-%02d %02d:%02d:%02d", sysTime.wYear, + sysTime.wMonth, sysTime.wDay, sysTime.wHour, + sysTime.wMinute, sysTime.wSecond); if (written > 0 && static_cast(written) < buffer.size()) { fileTimes.first = std::string(buffer.data()); @@ -1407,10 +1404,9 @@ template } template -[[nodiscard]] auto checkFileTypeInFolder(const P& folderPath, - std::span fileTypes, - FileOption fileOption) - -> std::vector { +[[nodiscard]] auto checkFileTypeInFolder( + const P& folderPath, std::span fileTypes, + FileOption fileOption) -> std::vector { spdlog::info("checkFileTypeInFolder called with folderPath: {}", fs::path(folderPath).string()); diff --git a/atom/io/core/path_utils.hpp b/atom/io/core/path_utils.hpp index a4a76bb2..f8f8128b 100644 --- a/atom/io/core/path_utils.hpp +++ b/atom/io/core/path_utils.hpp @@ -29,16 +29,16 @@ namespace fs = std::filesystem; // Platform-specific path validation regexes #ifdef _WIN32 -// Windows: disallow ?, *, :, ;, {}, \ in folder names (except drive letter colon) +// Windows: disallow ?, *, :, ;, {}, \ in folder names (except drive letter +// colon) inline const std::regex FOLDER_NAME_REGEX(R"(^[^\/?*:;{}\\]+[^\\]*$)"); // Windows: disallow \, /, :, *, ?, ", <, >, | in file names inline const std::regex FILE_NAME_REGEX("^[^\\/:*?\"<>|]+$"); // Windows reserved names inline const std::array RESERVED_NAMES = { - "CON", "PRN", "AUX", "NUL", - "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", - "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9" -}; + "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", + "COM5", "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3", + "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9"}; #else // Unix/Linux: disallow / in folder and file names inline const std::regex FOLDER_NAME_REGEX("^[^/]+$"); @@ -71,7 +71,8 @@ inline bool validatePath(std::string_view path) noexcept { } // Check for excessively long paths - constexpr size_t MAX_PATH_LENGTH = 4096; // Reasonable limit for most systems + constexpr size_t MAX_PATH_LENGTH = + 4096; // Reasonable limit for most systems if (path.length() > MAX_PATH_LENGTH) { spdlog::warn("Path exceeds maximum length: {}", path.length()); return false; @@ -99,14 +100,14 @@ inline bool validatePath(std::string_view path) noexcept { try { fs::path p(path); std::string filename = p.filename().string(); - + // Convert to uppercase for comparison std::string upper_filename = filename; std::transform(upper_filename.begin(), upper_filename.end(), - upper_filename.begin(), ::toupper); - + upper_filename.begin(), ::toupper); + for (const auto& reserved : RESERVED_NAMES) { - if (upper_filename == reserved || + if (upper_filename == reserved || upper_filename.starts_with(std::string(reserved) + ".")) { spdlog::warn("Path uses Windows reserved name: {}", filename); return false; @@ -143,7 +144,7 @@ inline bool isFolderNameValid(std::string_view folderName) noexcept { try { return std::regex_match(folderName.begin(), folderName.end(), - FOLDER_NAME_REGEX); + FOLDER_NAME_REGEX); } catch (const std::exception& e) { spdlog::error("Error checking folder name validity: {}", e.what()); return false; @@ -164,7 +165,7 @@ inline bool isFileNameValid(std::string_view fileName) noexcept { try { return std::regex_match(fileName.begin(), fileName.end(), - FILE_NAME_REGEX); + FILE_NAME_REGEX); } catch (const std::exception& e) { spdlog::error("Error checking file name validity: {}", e.what()); return false; @@ -198,8 +199,8 @@ inline bool isValidPath(const fs::path& path) noexcept { * @param write_access Whether write access is required * @return true if permissions are valid, false otherwise */ -inline bool validatePermissions(std::string_view path, - bool write_access = false) noexcept { +inline bool validatePermissions(std::string_view path, + bool write_access = false) noexcept { if (!validatePath(path)) { return false; } @@ -207,7 +208,7 @@ inline bool validatePermissions(std::string_view path, try { fs::path p(path); std::error_code ec; - + // Check if path exists if (!fs::exists(p, ec) || ec) { return false; @@ -225,7 +226,7 @@ inline bool validatePermissions(std::string_view path, } // Check write permission if required - if (write_access && + if (write_access && (perms & fs::perms::owner_write) == fs::perms::none) { return false; } @@ -240,4 +241,3 @@ inline bool validatePermissions(std::string_view path, } // namespace atom::io::path_utils #endif // ATOM_IO_CORE_PATH_UTILS_HPP - diff --git a/atom/io/filesystem/file_info.cpp b/atom/io/filesystem/file_info.cpp index c30daa38..9bcbb265 100644 --- a/atom/io/filesystem/file_info.cpp +++ b/atom/io/filesystem/file_info.cpp @@ -34,14 +34,16 @@ auto getFileInfo(const fs::path& filePath) -> FileInfo { FileInfo info; - // Edge case: Use error_code version to handle permission issues gracefully + // Edge case: Use error_code version to handle permission issues + // gracefully std::error_code ec; if (!fs::exists(filePath, ec) || ec) { - spdlog::error("File does not exist or is inaccessible: {} (error: {})", - filePath.string(), ec ? ec.message() : "unknown"); - throw std::runtime_error("File does not exist or is inaccessible: " + - filePath.string() + " (error: " + - (ec ? ec.message() : "unknown") + ")"); + spdlog::error( + "File does not exist or is inaccessible: {} (error: {})", + filePath.string(), ec ? ec.message() : "unknown"); + throw std::runtime_error( + "File does not exist or is inaccessible: " + filePath.string() + + " (error: " + (ec ? ec.message() : "unknown") + ")"); } // Edge case: Handle absolute path conversion errors @@ -56,7 +58,8 @@ auto getFileInfo(const fs::path& filePath) -> FileInfo { auto file_size = fs::file_size(filePath, ec); info.fileSize = ec ? 0 : file_size; } else { - info.fileSize = 0; // Directories and special files don't have meaningful size + info.fileSize = + 0; // Directories and special files don't have meaningful size } if (fs::is_directory(filePath)) { @@ -117,14 +120,9 @@ auto getFileInfo(const fs::path& filePath) -> FileInfo { PSECURITY_DESCRIPTOR pSD = nullptr; DWORD dwRtnCode = GetNamedSecurityInfoA( - filePath.string().c_str(), - SE_FILE_OBJECT, - OWNER_SECURITY_INFORMATION, - &pSidOwner, - nullptr, - nullptr, - nullptr, - &pSD); + filePath.string().c_str(), SE_FILE_OBJECT, + OWNER_SECURITY_INFORMATION, &pSidOwner, nullptr, + nullptr, nullptr, &pSD); if (dwRtnCode != ERROR_SUCCESS) { return "Unknown"; @@ -136,14 +134,19 @@ auto getFileInfo(const fs::path& filePath) -> FileInfo { DWORD dwDomainName = sizeof(szDomainName); SID_NAME_USE eUse = SidTypeUnknown; - if (LookupAccountSidA(nullptr, pSidOwner, szAccountName, &dwAcctName, - szDomainName, &dwDomainName, &eUse)) { - std::string result = std::string(szDomainName) + "\\" + std::string(szAccountName); - if (pSD) LocalFree(pSD); + if (LookupAccountSidA(nullptr, pSidOwner, szAccountName, + &dwAcctName, szDomainName, + &dwDomainName, &eUse)) { + std::string result = std::string(szDomainName) + + "\\" + + std::string(szAccountName); + if (pSD) + LocalFree(pSD); return result; } - if (pSD) LocalFree(pSD); + if (pSD) + LocalFree(pSD); return "Unknown"; } catch (...) { return "Unknown"; @@ -319,20 +322,24 @@ void deleteFile(const fs::path& filePath) { try { if (!fs::exists(filePath)) { spdlog::error("File does not exist: {}", filePath.string()); - throw std::runtime_error("File does not exist: " + filePath.string()); + throw std::runtime_error("File does not exist: " + + filePath.string()); } if (!fs::remove(filePath)) { spdlog::error("Failed to delete file: {}", filePath.string()); - throw std::runtime_error("Failed to delete file: " + filePath.string()); + throw std::runtime_error("Failed to delete file: " + + filePath.string()); } spdlog::info("Successfully deleted file: {}", filePath.string()); } catch (const fs::filesystem_error& e) { - spdlog::error("Filesystem error deleting file {}: {}", filePath.string(), e.what()); - throw std::runtime_error("Failed to delete file '" + filePath.string() + "': " + e.what()); + spdlog::error("Filesystem error deleting file {}: {}", + filePath.string(), e.what()); + throw std::runtime_error("Failed to delete file '" + filePath.string() + + "': " + e.what()); } catch (...) { - throw; // Re-throw any existing runtime_error or other exceptions + throw; // Re-throw any existing runtime_error or other exceptions } } diff --git a/atom/io/filesystem/file_permission.cpp b/atom/io/filesystem/file_permission.cpp index 84e9c5e9..257f0eef 100644 --- a/atom/io/filesystem/file_permission.cpp +++ b/atom/io/filesystem/file_permission.cpp @@ -232,7 +232,7 @@ std::string getFilePermissions(std::string_view filePath) noexcept { } try { - struct stat fileStat{}; + struct stat fileStat {}; if (stat(filePath.data(), &fileStat) < 0) { spdlog::error("stat failed for '{}': {}", filePath, strerror(errno)); diff --git a/atom/log/CMakeLists.txt b/atom/log/CMakeLists.txt index 46aa6016..4b2c4573 100644 --- a/atom/log/CMakeLists.txt +++ b/atom/log/CMakeLists.txt @@ -14,20 +14,34 @@ loguru_get_version_from_header() # defines LOGURU_VERSION # ---------------------------------------------------------- set(_namespace loguru) -project(loguru VERSION "${LOGURU_VERSION}" LANGUAGES CXX) - -set(LOGURU_PACKAGE_URL "https://github.com/emilk/loguru" CACHE STRING "") -set(LOGURU_PACKAGE_VENDOR "Emil Ernerfeldt" CACHE STRING "") -set(LOGURU_PACKAGE_CONTACT "Emil Ernerfeldt " CACHE STRING "") -set(LOGURU_PACKAGE_DESCRIPTION_SUMMARY "A lightweight C++ logging library" CACHE STRING "") -set(LOGURU_PACKAGE_DESCRIPTION_FILE "${PROJECT_SOURCE_DIR}/README.md" CACHE STRING "") +project( + loguru + VERSION "${LOGURU_VERSION}" + LANGUAGES CXX) + +set(LOGURU_PACKAGE_URL + "https://github.com/emilk/loguru" + CACHE STRING "") +set(LOGURU_PACKAGE_VENDOR + "Emil Ernerfeldt" + CACHE STRING "") +set(LOGURU_PACKAGE_CONTACT + "Emil Ernerfeldt " + CACHE STRING "") +set(LOGURU_PACKAGE_DESCRIPTION_SUMMARY + "A lightweight C++ logging library" + CACHE STRING "") +set(LOGURU_PACKAGE_DESCRIPTION_FILE + "${PROJECT_SOURCE_DIR}/README.md" + CACHE STRING "") # --- check if toplevel or subdirectory # ---------------------------------------------------------- # This variable is set automatically by the project() call in CMake 3.21+ -string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${PROJECT_SOURCE_DIR}" PROJECT_IS_TOP_LEVEL) -if (PROJECT_IS_TOP_LEVEL) +string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${PROJECT_SOURCE_DIR}" + PROJECT_IS_TOP_LEVEL) +if(PROJECT_IS_TOP_LEVEL) message(STATUS "Configuring ${PROJECT_NAME} as top-level") else() message(STATUS "Configuring ${PROJECT_NAME} as sub-directory") @@ -36,11 +50,13 @@ endif() # --- set default build type # ---------------------------------------------------------- -# NOTE: when running as a standalone project, we only allow Release & Debug -# but as a sub-project we don't want to accidentally pollute the parent -if (PROJECT_IS_TOP_LEVEL) +# NOTE: when running as a standalone project, we only allow Release & Debug but +# as a sub-project we don't want to accidentally pollute the parent +if(PROJECT_IS_TOP_LEVEL) if(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose Release or Debug" FORCE) + set(CMAKE_BUILD_TYPE + "Release" + CACHE STRING "Choose Release or Debug" FORCE) endif() set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Release;Debug") endif() @@ -48,31 +64,32 @@ endif() # --- expose cmake-specific user options # ---------------------------------------------------------- -option(LOGURU_INSTALL "Generate the install target(s)" ${PROJECT_IS_TOP_LEVEL}) -option(LOGURU_BUILD_EXAMPLES "Build the project examples" ${PROJECT_IS_TOP_LEVEL}) -option(LOGURU_BUILD_TESTS "Build the tests" ${PROJECT_IS_TOP_LEVEL}) -if (LOGURU_INSTALL) +option(LOGURU_INSTALL "Generate the install target(s)" ${PROJECT_IS_TOP_LEVEL}) +option(LOGURU_BUILD_EXAMPLES "Build the project examples" + ${PROJECT_IS_TOP_LEVEL}) +option(LOGURU_BUILD_TESTS "Build the tests" ${PROJECT_IS_TOP_LEVEL}) +if(LOGURU_INSTALL) option(LOGURU_CPACK "Generate CPackConfig.cmake" ${PROJECT_IS_TOP_LEVEL}) endif() # --- set global compile flags # ---------------------------------------------------------- -if (PROJECT_IS_TOP_LEVEL) +if(PROJECT_IS_TOP_LEVEL) # enable ALL warnings for all subsequently defined targets add_compile_options( "$<$:-Wall;-Wextra;-Werror;-pedantic>" "$<$:-Weverything;-Wno-c++98-compat;-Wno-c++98-compat-pedantic>" - "$<$:/W4>" - ) + "$<$:/W4>") endif() # --- add loguru target # ---------------------------------------------------------- -add_library(loguru SHARED loguru.cpp) # allow BUILD_SHARED_LIBS to decide STATIC/SHARED +add_library(loguru SHARED loguru.cpp) # allow BUILD_SHARED_LIBS to decide + # STATIC/SHARED -if (NOT PROJECT_IS_TOP_LEVEL) +if(NOT PROJECT_IS_TOP_LEVEL) add_library(${_namespace}::loguru ALIAS loguru) endif() @@ -81,7 +98,7 @@ endif() set(LOGURU_USE_FMTLIB Off) -if (WIN32) +if(WIN32) find_package(dlfcn-win32 QUIET) if(dlfcn-win32_FOUND) set(CMAKE_DL_LIBS dlfcn-win32::dl) @@ -89,17 +106,17 @@ if (WIN32) else() message(STATUS "dlfcn-win32 not found - some features may be limited") endif() -endif () +endif() -if (LOGURU_STACKTRACES AND (NOT CMAKE_DL_LIBS)) - message(WARNING - "Stack traces requested but the required 'dl' library was not found. " - "LOGURU_STACKTRACES has been automatically disabled (set to 0)" - ) +if(LOGURU_STACKTRACES AND (NOT CMAKE_DL_LIBS)) + message( + WARNING + "Stack traces requested but the required 'dl' library was not found. " + "LOGURU_STACKTRACES has been automatically disabled (set to 0)") set(LOGURU_STACKTRACES 0) endif() -if (LOGURU_STACKTRACES) +if(LOGURU_STACKTRACES) set(_lib_dl_linkflag "-l${CMAKE_DL_LIBS}") else() set(_lib_dl_linkflag) # dl dependency is not needed if STACKTRACES=0 @@ -109,42 +126,37 @@ endif() # ---------------------------------------------------------- target_include_directories(loguru - PUBLIC - $ -) + PUBLIC $) target_compile_features(loguru PUBLIC cxx_std_11) find_package(Threads REQUIRED) # defines IMPORTED target Threads::Threads if(WIN32) -target_link_libraries(loguru - PUBLIC - Threads::Threads # pthreads (or equivalent) - dbghelp -) -if(dlfcn-win32_FOUND) + target_link_libraries( + loguru PUBLIC Threads::Threads # pthreads (or equivalent) + dbghelp) + if(dlfcn-win32_FOUND) target_link_libraries(loguru PUBLIC dlfcn-win32::dl) -endif() + endif() else() -target_link_libraries(loguru - PUBLIC - Threads::Threads # pthreads (or equivalent) - dl - ${_lib_dl_linkflag} # dl (or equivalent) -) + target_link_libraries( + loguru PUBLIC Threads::Threads # pthreads (or equivalent) + dl ${_lib_dl_linkflag} # dl (or equivalent) + ) endif() -set_target_properties(loguru - PROPERTIES - VERSION "${LOGURU_VERSION}" - SOVERSION "${LOGURU_VERSION_MAJOR}" - DEBUG_POSTFIX "d" -) +set_target_properties( + loguru + PROPERTIES VERSION "${LOGURU_VERSION}" + SOVERSION "${LOGURU_VERSION_MAJOR}" + DEBUG_POSTFIX "d") -target_compile_definitions(loguru +target_compile_definitions( + loguru # NOTE: these generator expressions are dense but the logic is quite simple! - # if any of the cache variables are not equal to the empty string, set them as a definition. - # Additionally, the "boolean" variables are coerced into a numeric representation (1 or 0) + # if any of the cache variables are not equal to the empty string, set them as + # a definition. Additionally, the "boolean" variables are coerced into a + # numeric representation (1 or 0) PUBLIC $<$>:LOGURU_EXPORT=${LOGURU_EXPORT}> $<$>:LOGURU_DEBUG_LOGGING=$> @@ -167,15 +179,15 @@ target_compile_definitions(loguru # --- import and link fmt (if needed) # ---------------------------------------------------------- -if (LOGURU_USE_FMTLIB) +if(LOGURU_USE_FMTLIB) message(STATUS "linking to fmt") - if (NOT TARGET fmt::fmt) # only search if not already found in parent scope + if(NOT TARGET fmt::fmt) # only search if not already found in parent scope find_package(fmt CONFIG REQUIRED) endif() - if (LOGURU_FMT_HEADER_ONLY) + if(LOGURU_FMT_HEADER_ONLY) target_link_libraries(loguru PUBLIC fmt::fmt-header-only) else() target_link_libraries(loguru PUBLIC fmt::fmt) @@ -189,20 +201,19 @@ endif() # ---------------------------------------------------------- # make the project the default when opened in visual studio ide -set_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ${PROJECT_NAME}) +set_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT + ${PROJECT_NAME}) # --- setup examples # ---------------------------------------------------------- # TODO: make the examples work with this cmake paradigm -if (LOGURU_BUILD_EXAMPLES) +if(LOGURU_BUILD_EXAMPLES) message(STATUS "!!! the examples don't work with this cmake build yet") # message(STATUS "building examples") - # add_subdirectory(glog_bench) - # add_subdirectory(glog_example) - # add_subdirectory(loguru_bench) - # add_subdirectory(loguru_example) + # add_subdirectory(glog_bench) add_subdirectory(glog_example) + # add_subdirectory(loguru_bench) add_subdirectory(loguru_example) # message(STATUS "building examples - done") endif() @@ -211,17 +222,16 @@ endif() # ---------------------------------------------------------- # TODO: make the tests work with this cmake paradigm -if (LOGURU_BUILD_TESTS) +if(LOGURU_BUILD_TESTS) message(STATUS "!!! the tests don't work with this cmake build yet") - # message(STATUS "building tests") - # add_subdirectory(test) - # message(STATUS "building tests - done") + # message(STATUS "building tests") add_subdirectory(test) message(STATUS + # "building tests - done") endif() # --- setup install rules # ---------------------------------------------------------- -if (LOGURU_INSTALL) +if(LOGURU_INSTALL) message(STATUS "generating install rules") @@ -232,107 +242,116 @@ if (LOGURU_INSTALL) # -- expose cache variables for users to customize install location - set(LOGURU_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" CACHE STRING - "Install directory for cmake files, relative to \${CMAKE_INSTALL_PREFIX} or an absolute path") - set(LOGURU_INSTALL_LIBDIR "${CMAKE_INSTALL_LIBDIR}" CACHE STRING - "Install directory for libraries, relative to \${CMAKE_INSTALL_PREFIX} or an absolute path") - set(LOGURU_INSTALL_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}" CACHE STRING - "Install directory for include files, relative to \${CMAKE_INSTALL_PREFIX} or an absolute path") - set(LOGURU_INSTALL_PKGCONFIGDIR "${CMAKE_INSTALL_LIBDIR}/pkgconfig" CACHE STRING - "Install directory for pkgconfig (.pc) files, relative to \${CMAKE_INSTALL_PREFIX} or an absolute path") + set(LOGURU_INSTALL_CMAKEDIR + "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" + CACHE + STRING + "Install directory for cmake files, relative to \${CMAKE_INSTALL_PREFIX} or an absolute path" + ) + set(LOGURU_INSTALL_LIBDIR + "${CMAKE_INSTALL_LIBDIR}" + CACHE + STRING + "Install directory for libraries, relative to \${CMAKE_INSTALL_PREFIX} or an absolute path" + ) + set(LOGURU_INSTALL_INCLUDEDIR + "${CMAKE_INSTALL_INCLUDEDIR}" + CACHE + STRING + "Install directory for include files, relative to \${CMAKE_INSTALL_PREFIX} or an absolute path" + ) + set(LOGURU_INSTALL_PKGCONFIGDIR + "${CMAKE_INSTALL_LIBDIR}/pkgconfig" + CACHE + STRING + "Install directory for pkgconfig (.pc) files, relative to \${CMAKE_INSTALL_PREFIX} or an absolute path" + ) # -- set additional target properties relevant to install dir - target_include_directories(loguru - PUBLIC - $ - ) + target_include_directories( + loguru PUBLIC $) # -- setup install config files - set(_project_config_file_in ${PROJECT_SOURCE_DIR}/cmake/${PROJECT_NAME}-config.cmake.in) - set(_project_config_file_out ${PROJECT_BINARY_DIR}/${PROJECT_NAME}-config.cmake) - set(_version_config_file ${PROJECT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake) + set(_project_config_file_in + ${PROJECT_SOURCE_DIR}/cmake/${PROJECT_NAME}-config.cmake.in) + set(_project_config_file_out + ${PROJECT_BINARY_DIR}/${PROJECT_NAME}-config.cmake) + set(_version_config_file + ${PROJECT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake) set(_targets_export_name ${PROJECT_NAME}-targets) - set(_pkgconfig_file_in ${PROJECT_SOURCE_DIR}/cmake/${PROJECT_NAME}.pc.in) - set(_pkgconfig_file_out ${PROJECT_BINARY_DIR}/${PROJECT_NAME}.pc) + set(_pkgconfig_file_in ${PROJECT_SOURCE_DIR}/cmake/${PROJECT_NAME}.pc.in) + set(_pkgconfig_file_out ${PROJECT_BINARY_DIR}/${PROJECT_NAME}.pc) # -- Configure pkg-config template - set(_pkgconfig_libdir "\${exec_prefix}/${LOGURU_INSTALL_LIBDIR}") + set(_pkgconfig_libdir "\${exec_prefix}/${LOGURU_INSTALL_LIBDIR}") set(_pkgconfig_includedir "\${prefix}/${LOGURU_INSTALL_INCLUDEDIR}") # if the user chose absolute paths, strip the ${prefix} and/or ${exec_prefix} - if (IS_ABSOLUTE "${LOGURU_INSTALL_LIBDIR}") + if(IS_ABSOLUTE "${LOGURU_INSTALL_LIBDIR}") set(_pkgconfig_libdir "${LOGURU_INSTALL_LIBDIR}") endif() - if (IS_ABSOLUTE "${LOGURU_INSTALL_INCLUDEDIR}") + if(IS_ABSOLUTE "${LOGURU_INSTALL_INCLUDEDIR}") set(_pkgconfig_includedir "${LOGURU_INSTALL_INCLUDEDIR}") endif() - configure_file( - ${_pkgconfig_file_in} - ${_pkgconfig_file_out} - @ONLY - ) + configure_file(${_pkgconfig_file_in} ${_pkgconfig_file_out} @ONLY) # -- Generate the version file in the build directory - write_basic_package_version_file( # function from CMakePackageConfigHelpers - ${_version_config_file} - COMPATIBILITY SameMajorVersion - ) + write_basic_package_version_file( + # function from CMakePackageConfigHelpers + ${_version_config_file} COMPATIBILITY SameMajorVersion) # -- Generate the config file in the build directory - configure_package_config_file( # function from CMakePackageConfigHelpers - ${_project_config_file_in} - ${_project_config_file_out} - INSTALL_DESTINATION ${LOGURU_INSTALL_CMAKEDIR} - ) + configure_package_config_file( + # function from CMakePackageConfigHelpers + ${_project_config_file_in} ${_project_config_file_out} + INSTALL_DESTINATION ${LOGURU_INSTALL_CMAKEDIR}) # -- Install the main library - install(TARGETS loguru - EXPORT ${_targets_export_name} # Add this target to the 'exports' file - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} # .dll, .exe + install( + TARGETS loguru + EXPORT ${_targets_export_name} # Add this target to the 'exports' file + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} # .dll, .exe ARCHIVE DESTINATION ${LOGURU_INSTALL_LIBDIR} # .lib, .a LIBRARY DESTINATION ${LOGURU_INSTALL_LIBDIR} # .so ) # -- Install the header file - install(FILES loguru.hpp - DESTINATION ${LOGURU_INSTALL_INCLUDEDIR}/loguru - ) + install(FILES loguru.hpp DESTINATION ${LOGURU_INSTALL_INCLUDEDIR}/loguru) # -- Install version and config files - install(FILES ${_project_config_file_out} ${_version_config_file} - DESTINATION ${LOGURU_INSTALL_CMAKEDIR} - ) + install(FILES ${_project_config_file_out} ${_version_config_file} + DESTINATION ${LOGURU_INSTALL_CMAKEDIR}) # -- Install pkgconfig file install(FILES ${_pkgconfig_file_out} - DESTINATION ${LOGURU_INSTALL_PKGCONFIGDIR} - ) + DESTINATION ${LOGURU_INSTALL_PKGCONFIGDIR}) # -- Install target exports file - install(EXPORT ${_targets_export_name} + install( + EXPORT ${_targets_export_name} NAMESPACE ${_namespace}:: - DESTINATION ${LOGURU_INSTALL_CMAKEDIR} - ) + DESTINATION ${LOGURU_INSTALL_CMAKEDIR}) # -- Install .pdb file (if exists) - if (MSVC AND BUILD_SHARED_LIBS) - install(FILES $ + if(MSVC AND BUILD_SHARED_LIBS) + install( + FILES $ CONFIGURATIONS "Debug" - DESTINATION ${LOGURU_INSTALL_LIBDIR} OPTIONAL - ) + DESTINATION ${LOGURU_INSTALL_LIBDIR} + OPTIONAL) endif() message(STATUS "generating install rules - done") @@ -342,35 +361,25 @@ endif() # LOGURU_INSTALL # --- Add atom-log library # ---------------------------------------------------------- -# Only build atom-log if we're not the top-level project (i.e., we're being included by Atom) -if (NOT PROJECT_IS_TOP_LEVEL) +# Only build atom-log if we're not the top-level project (i.e., we're being +# included by Atom) +if(NOT PROJECT_IS_TOP_LEVEL) # Include standardized module configuration include(${CMAKE_SOURCE_DIR}/cmake/ModuleDependencies.cmake) # Define atom-log sources - set(ATOM_LOG_SOURCES - async_logger.cpp - atomlog.cpp - log_manager.cpp - logger.cpp - mmap_logger.cpp - ) + set(ATOM_LOG_SOURCES async_logger.cpp atomlog.cpp log_manager.cpp logger.cpp + mmap_logger.cpp) # Define atom-log headers - set(ATOM_LOG_HEADERS - async_logger.hpp - atomlog.hpp - log_manager.hpp - logger.hpp - mmap_logger.hpp - ) + set(ATOM_LOG_HEADERS async_logger.hpp atomlog.hpp log_manager.hpp logger.hpp + mmap_logger.hpp) # Create atom-log library add_library(atom-log SHARED ${ATOM_LOG_SOURCES} ${ATOM_LOG_HEADERS}) - # Link dependencies - # Find and link fmt if available (required for spdlog) + # Link dependencies Find and link fmt if available (required for spdlog) find_package(fmt QUIET) if(fmt_FOUND) message(STATUS "fmt found - linking to atom-log") @@ -380,25 +389,15 @@ if (NOT PROJECT_IS_TOP_LEVEL) atom_configure_module(atom-log) # Link module-specific dependencies - target_link_libraries(atom-log - PUBLIC - loguru - atom-error - atom-utils - atom-web - ZLIB::ZLIB - $<$:fmt::fmt> - ) + target_link_libraries( + atom-log PUBLIC loguru atom-error atom-utils atom-web ZLIB::ZLIB + $<$:fmt::fmt>) - target_compile_definitions(atom-log - PUBLIC - SPDLOG_USE_STD_FORMAT=1 - ) + target_compile_definitions(atom-log PUBLIC SPDLOG_USE_STD_FORMAT=1) # Install headers install(FILES ${ATOM_LOG_HEADERS} - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/atom/log - ) + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/atom/log) message(STATUS "atom-log library configured") @@ -407,7 +406,7 @@ endif() # -- Setup CPack # ---------------------------------------------------------- -if (LOGURU_INSTALL AND LOGURU_CPACK) +if(LOGURU_INSTALL AND LOGURU_CPACK) message(STATUS "setting up cpack") diff --git a/atom/log/async_logger.cpp b/atom/log/async_logger.cpp index cdfaa2b4..596327c4 100644 --- a/atom/log/async_logger.cpp +++ b/atom/log/async_logger.cpp @@ -46,8 +46,8 @@ namespace atom::log { using json = nlohmann::json; struct LoggerMemoryPool { - static constexpr size_t BLOCK_SIZE = 8192; // 8KB blocks - static constexpr size_t MAX_BLOCKS = 2048; // Max 16MB total + static constexpr size_t BLOCK_SIZE = 8192; // 8KB blocks + static constexpr size_t MAX_BLOCKS = 2048; // Max 16MB total static constexpr size_t INITIAL_BLOCKS = 16; // 预分配块提高启动性能 // 线程安全的单例访问 @@ -106,7 +106,8 @@ struct LoggerMemoryPool { pool_.deallocate(ptr); } - bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override { + bool do_is_equal( + const std::pmr::memory_resource& other) const noexcept override { return this == &other; } diff --git a/atom/log/async_logger.hpp b/atom/log/async_logger.hpp index b09e8c68..2ff9edaf 100644 --- a/atom/log/async_logger.hpp +++ b/atom/log/async_logger.hpp @@ -20,7 +20,6 @@ Description: Enhanced Asynchronous Logger using C++20/23 Coroutines #include #include -#include "atom/type/compat.hpp" #include #include #include @@ -28,6 +27,7 @@ Description: Enhanced Asynchronous Logger using C++20/23 Coroutines #include #include #include +#include "atom/type/compat.hpp" namespace fs = std::filesystem; @@ -83,7 +83,8 @@ class Task { public: // Promise type that satisfies C++20 coroutine promise concept struct promise_type { - atom::type::expected result{T{}, LogErrorCode::Success}; + atom::type::expected result{T{}, + LogErrorCode::Success}; Task get_return_object() { return Task( @@ -291,8 +292,8 @@ class AsyncLogger { co_return co_await logAsync(LogLevel::DEBUG_LEVEL, std::move(msg), location); } else { - co_return co_await logAsync(LogLevel::DEBUG_LEVEL, std::string(format), - location); + co_return co_await logAsync(LogLevel::DEBUG_LEVEL, + std::string(format), location); } } @@ -314,8 +315,8 @@ class AsyncLogger { co_return co_await logAsync(LogLevel::INFO_LEVEL, std::move(msg), location); } else { - co_return co_await logAsync(LogLevel::INFO_LEVEL, std::string(format), - location); + co_return co_await logAsync(LogLevel::INFO_LEVEL, + std::string(format), location); } } @@ -337,8 +338,8 @@ class AsyncLogger { co_return co_await logAsync(LogLevel::WARN_LEVEL, std::move(msg), location); } else { - co_return co_await logAsync(LogLevel::WARN_LEVEL, std::string(format), - location); + co_return co_await logAsync(LogLevel::WARN_LEVEL, + std::string(format), location); } } @@ -360,8 +361,8 @@ class AsyncLogger { co_return co_await logAsync(LogLevel::ERROR_LEVEL, std::move(msg), location); } else { - co_return co_await logAsync(LogLevel::ERROR_LEVEL, std::string(format), - location); + co_return co_await logAsync(LogLevel::ERROR_LEVEL, + std::string(format), location); } } @@ -380,11 +381,11 @@ class AsyncLogger { std::source_location::current()) { if constexpr (sizeof...(args) > 0) { auto msg = std::format(format.c_str(), std::forward(args)...); - co_return co_await logAsync(LogLevel::CRITICAL_LEVEL, std::move(msg), - location); + co_return co_await logAsync(LogLevel::CRITICAL_LEVEL, + std::move(msg), location); } else { - co_return co_await logAsync(LogLevel::CRITICAL_LEVEL, std::string(format), - location); + co_return co_await logAsync(LogLevel::CRITICAL_LEVEL, + std::string(format), location); } } diff --git a/atom/log/atomlog.cpp b/atom/log/atomlog.cpp index bf7ab5e1..9a8bd280 100644 --- a/atom/log/atomlog.cpp +++ b/atom/log/atomlog.cpp @@ -61,9 +61,12 @@ constexpr std::string_view logLevelToString(LogLevel level) noexcept { LogLevel stringToLogLevel(std::string_view str) { static const std::unordered_map level_map = { - {"TRACE", LogLevel::TRACE}, {"DEBUG", LogLevel::DEBUG_LEVEL}, - {"INFO", LogLevel::INFO_LEVEL}, {"WARN", LogLevel::WARN_LEVEL}, - {"ERROR", LogLevel::ERROR_LEVEL}, {"CRITICAL", LogLevel::CRITICAL_LEVEL}, + {"TRACE", LogLevel::TRACE}, + {"DEBUG", LogLevel::DEBUG_LEVEL}, + {"INFO", LogLevel::INFO_LEVEL}, + {"WARN", LogLevel::WARN_LEVEL}, + {"ERROR", LogLevel::ERROR_LEVEL}, + {"CRITICAL", LogLevel::CRITICAL_LEVEL}, {"OFF", LogLevel::OFF_LEVEL}}; auto it = level_map.find(str); diff --git a/atom/log/atomlog.hpp b/atom/log/atomlog.hpp index 2555bea9..59face2b 100644 --- a/atom/log/atomlog.hpp +++ b/atom/log/atomlog.hpp @@ -36,13 +36,13 @@ using atom::containers::String; * Extended to support custom log levels. */ enum class LogLevel : int { - TRACE = 0, ///< Trace level logging. - DEBUG_LEVEL, ///< Debug level logging. - INFO_LEVEL, ///< Info level logging. - WARN_LEVEL, ///< Warn level logging. - ERROR_LEVEL, ///< Error level logging. - CRITICAL_LEVEL,///< Critical level logging. - OFF_LEVEL ///< Used to disable logging. + TRACE = 0, ///< Trace level logging. + DEBUG_LEVEL, ///< Debug level logging. + INFO_LEVEL, ///< Info level logging. + WARN_LEVEL, ///< Warn level logging. + ERROR_LEVEL, ///< Error level logging. + CRITICAL_LEVEL, ///< Critical level logging. + OFF_LEVEL ///< Used to disable logging. }; /** diff --git a/atom/log/cmake/loguru-cpack.cmake b/atom/log/cmake/loguru-cpack.cmake index d7b68fff..75995d2b 100644 --- a/atom/log/cmake/loguru-cpack.cmake +++ b/atom/log/cmake/loguru-cpack.cmake @@ -1,29 +1,32 @@ # -- expose cache variables to the user # Set .zip and tar.gz as default generators -set(LOGURU_CPACK_GENERATOR "TGZ;ZIP" CACHE STRING - "Semicolon separated list of generators") +set(LOGURU_CPACK_GENERATOR + "TGZ;ZIP" + CACHE STRING "Semicolon separated list of generators") # NOTE: CPACK_PACKAGE_DIRECTORY normally defaults to CMAKE_BINARY_DIR -set(LOGURU_CPACK_PACKAGE_DIRECTORY "${PROJECT_BINARY_DIR}/packages" CACHE PATH - "Where to generate loguru cpack installer packages") +set(LOGURU_CPACK_PACKAGE_DIRECTORY + "${PROJECT_BINARY_DIR}/packages" + CACHE PATH "Where to generate loguru cpack installer packages") set(CPACK_GENERATOR ${LOGURU_CPACK_GENERATOR}) set(CPACK_PACKAGE_DIRECTORY ${LOGURU_CPACK_PACKAGE_DIRECTORY}) # -- contact and summary -set(CPACK_PROJECT_URL "${LOGURU_PACKAGE_URL}") -set(CPACK_PACKAGE_VENDOR "${LOGURU_PACKAGE_VENDOR}") +set(CPACK_PROJECT_URL "${LOGURU_PACKAGE_URL}") +set(CPACK_PACKAGE_VENDOR "${LOGURU_PACKAGE_VENDOR}") set(CPACK_PACKAGE_CONTACT "${LOGURU_PACKAGE_CONTACT}") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "${LOGURU_PACKAGE_DESCRIPTION_SUMMARY}") -set(CPACK_PACKAGE_DESCRIPTION_FILE "${LOGURU_PACKAGE_DESCRIPTION_FILE}") +set(CPACK_PACKAGE_DESCRIPTION_FILE "${LOGURU_PACKAGE_DESCRIPTION_FILE}") # -- version info set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR}) set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR}) set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH}) -set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}) +set(CPACK_PACKAGE_VERSION + ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}) if(PROJECT_VERSION_TWEAK) set(CPACK_PACKAGE_VERSION ${CPACK_PACKAGE_VERSION}.${PROJECT_VERSION_TWEAK}) endif() @@ -36,20 +39,19 @@ set(CPACK_STRIP_FILES ON) # -- normalize paths # NOTE: some generators don't understand paths that contain backslashes -string(REPLACE "\\" "/" CPACK_PACKAGE_DIRECTORY "${CPACK_PACKAGE_DIRECTORY}" ) +string(REPLACE "\\" "/" CPACK_PACKAGE_DIRECTORY "${CPACK_PACKAGE_DIRECTORY}") # -- make cpack work with sub-projects -# NOTE: -# cpack assumes by default that it's being called from a top-level project. -# It uses the top-level project values to generate 'CPackConfig.cmake' and -# tries to place it into the top-level build directory. -# This behavior makes sense if you're writing a standalone cmake project, but -# causes grief if your project is designed to be included as a sub-project -# e.g. using add_subdirectory() or FetchContent() -# Here, we trick cpack into using the current current project info instead. -set(CMAKE_BINARY_DIR "${PROJECT_BINARY_DIR}") -set(CMAKE_SOURCE_DIR "${PROJECT_SOURCE_DIR}") +# NOTE: cpack assumes by default that it's being called from a top-level +# project. It uses the top-level project values to generate 'CPackConfig.cmake' +# and tries to place it into the top-level build directory. This behavior makes +# sense if you're writing a standalone cmake project, but causes grief if your +# project is designed to be included as a sub-project e.g. using +# add_subdirectory() or FetchContent() Here, we trick cpack into using the +# current current project info instead. +set(CMAKE_BINARY_DIR "${PROJECT_BINARY_DIR}") +set(CMAKE_SOURCE_DIR "${PROJECT_SOURCE_DIR}") set(CMAKE_PROJECT_NAME "${PROJECT_NAME}") # -- include CPack module diff --git a/atom/log/cmake/utilities.cmake b/atom/log/cmake/utilities.cmake index 24b64149..b0894bb7 100644 --- a/atom/log/cmake/utilities.cmake +++ b/atom/log/cmake/utilities.cmake @@ -4,26 +4,40 @@ function(loguru_get_version_from_header) file(READ "${CMAKE_CURRENT_LIST_DIR}/loguru.hpp" _hdr_contents) - string(REGEX REPLACE ".*LOGURU_VERSION_MAJOR ([0-9]+).*" "\\1" _version_major "${_hdr_contents}") - string(REGEX REPLACE ".*LOGURU_VERSION_MINOR ([0-9]+).*" "\\1" _version_minor "${_hdr_contents}") - string(REGEX REPLACE ".*LOGURU_VERSION_PATCH ([0-9]+).*" "\\1" _version_patch "${_hdr_contents}") + string(REGEX REPLACE ".*LOGURU_VERSION_MAJOR ([0-9]+).*" "\\1" _version_major + "${_hdr_contents}") + string(REGEX REPLACE ".*LOGURU_VERSION_MINOR ([0-9]+).*" "\\1" _version_minor + "${_hdr_contents}") + string(REGEX REPLACE ".*LOGURU_VERSION_PATCH ([0-9]+).*" "\\1" _version_patch + "${_hdr_contents}") if(_version_major STREQUAL "") - message(FATAL_ERROR "Could not extract major version number from loguru.hpp") + message( + FATAL_ERROR "Could not extract major version number from loguru.hpp") endif() if(_version_minor STREQUAL "") - message(FATAL_ERROR "Could not extract minor version number from loguru.hpp") + message( + FATAL_ERROR "Could not extract minor version number from loguru.hpp") endif() if(_version_patch STREQUAL "") - message(FATAL_ERROR "Could not extract patch version number from loguru.hpp") + message( + FATAL_ERROR "Could not extract patch version number from loguru.hpp") endif() - set(LOGURU_VERSION_MAJOR "${_version_major}" CACHE STRING "" FORCE) - set(LOGURU_VERSION_MINOR "${_version_minor}" CACHE STRING "" FORCE) - set(LOGURU_VERSION_PATCH "${_version_patch}" CACHE STRING "" FORCE) - set(LOGURU_VERSION "${_version_major}.${_version_minor}.${_version_patch}" CACHE STRING "" FORCE) + set(LOGURU_VERSION_MAJOR + "${_version_major}" + CACHE STRING "" FORCE) + set(LOGURU_VERSION_MINOR + "${_version_minor}" + CACHE STRING "" FORCE) + set(LOGURU_VERSION_PATCH + "${_version_patch}" + CACHE STRING "" FORCE) + set(LOGURU_VERSION + "${_version_major}.${_version_minor}.${_version_patch}" + CACHE STRING "" FORCE) endfunction() diff --git a/atom/log/loguru.cpp b/atom/log/loguru.cpp index 7e6a1a7a..501deea2 100644 --- a/atom/log/loguru.cpp +++ b/atom/log/loguru.cpp @@ -1102,8 +1102,8 @@ bool add_file(const char* path_in, FileMode mode, Verbosity verbosity, auto add_syslog(const char* app_name, Verbosity verbosity) -> bool { return add_syslog(app_name, verbosity, LOG_USER); } -auto add_syslog(const char* app_name, Verbosity verbosity, int facility) - -> bool { +auto add_syslog(const char* app_name, Verbosity verbosity, + int facility) -> bool { #if LOGURU_SYSLOG if (app_name == nullptr) { app_name = argv0_filename(); diff --git a/atom/log/loguru.hpp b/atom/log/loguru.hpp index 8f13f81e..af9d6685 100644 --- a/atom/log/loguru.hpp +++ b/atom/log/loguru.hpp @@ -620,8 +620,8 @@ auto add_syslog(const char* app_name, Verbosity verbosity) -> bool; LOGURU_EXPORT // Send logs to syslog with your own choice of facility (LOG_USER, LOG_AUTH, // ...) see loguru.cpp: syslog_log() for more details. -auto add_syslog(const char* app_name, Verbosity verbosity, int facility) - -> bool; +auto add_syslog(const char* app_name, Verbosity verbosity, + int facility) -> bool; /* Will be called right before abort(). You can for instance use this to print custom error messages, or throw diff --git a/atom/log/mmap_logger.cpp b/atom/log/mmap_logger.cpp index 4533fd60..41f1269f 100644 --- a/atom/log/mmap_logger.cpp +++ b/atom/log/mmap_logger.cpp @@ -743,12 +743,11 @@ class MmapLogger::MmapLoggerImpl { } // Format log message with optimized string handling - [[nodiscard]] auto formatMessage(LogLevel level, - MmapLogger::Category category, - std::string_view msg, - const std::source_location& location) - -> std::string { - auto timestamp = atom::utils::getChinaTimestampString(); // Get timestamp + [[nodiscard]] auto formatMessage( + LogLevel level, MmapLogger::Category category, std::string_view msg, + const std::source_location& location) -> std::string { + auto timestamp = + atom::utils::getChinaTimestampString(); // Get timestamp auto threadName = getThreadName(); auto levelStr = logLevelToString(level); auto categoryStr = categoryToString(category); diff --git a/atom/log/mmap_logger.hpp b/atom/log/mmap_logger.hpp index 8e449b2c..dfbfb128 100644 --- a/atom/log/mmap_logger.hpp +++ b/atom/log/mmap_logger.hpp @@ -20,12 +20,12 @@ Description: Memory-mapped File Logger for Atom with C++20 Features #include "atomlog.hpp" #include -#include "atom/type/compat.hpp" #include #include #include #include #include +#include "atom/type/compat.hpp" namespace fs = std::filesystem; diff --git a/atom/memory/CMakeLists.txt b/atom/memory/CMakeLists.txt index a9d73f11..bf158995 100644 --- a/atom/memory/CMakeLists.txt +++ b/atom/memory/CMakeLists.txt @@ -1,10 +1,11 @@ -# CMakeLists.txt for Memory Module -# Part of the Atom Project -# Author: Max Qian +# CMakeLists.txt for Memory Module Part of the Atom Project Author: Max Qian # License: GPL3 cmake_minimum_required(VERSION 3.21) -project(atom-memory VERSION 1.0.0 LANGUAGES C CXX) +project( + atom-memory + VERSION 1.0.0 + LANGUAGES C CXX) # Include standardized module configuration include(${CMAKE_SOURCE_DIR}/cmake/ModuleDependencies.cmake) @@ -15,34 +16,32 @@ file(GLOB_RECURSE HEADERS "*.h" "*.hpp") # Determine if this is header-only if(NOT SOURCES) - set(IS_HEADER_ONLY TRUE) + set(IS_HEADER_ONLY TRUE) else() - set(IS_HEADER_ONLY FALSE) + set(IS_HEADER_ONLY FALSE) endif() # Create library target if(IS_HEADER_ONLY) - add_library(atom-memory INTERFACE) - atom_configure_module(atom-memory HEADER_ONLY) + add_library(atom-memory INTERFACE) + atom_configure_module(atom-memory HEADER_ONLY) else() - add_library(atom-memory STATIC ${SOURCES} ${HEADERS}) - atom_configure_module(atom-memory) + add_library(atom-memory STATIC ${SOURCES} ${HEADERS}) + atom_configure_module(atom-memory) endif() # Link module-specific dependencies if(IS_HEADER_ONLY) - target_link_libraries(atom-memory INTERFACE - atom-meta # For concept.hpp - atom-type # For noncopyable.hpp - ) + target_link_libraries( + atom-memory INTERFACE atom-meta # For concept.hpp + atom-type # For noncopyable.hpp + ) else() - target_link_libraries(atom-memory PUBLIC - atom-meta # For concept.hpp - atom-type # For noncopyable.hpp - ) + target_link_libraries( + atom-memory PUBLIC atom-meta # For concept.hpp + atom-type # For noncopyable.hpp + ) endif() # Install headers -install(FILES ${HEADERS} - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/atom/memory -) +install(FILES ${HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/atom/memory) diff --git a/atom/memory/memory.hpp b/atom/memory/memory.hpp index 8f402168..3e4d84fa 100644 --- a/atom/memory/memory.hpp +++ b/atom/memory/memory.hpp @@ -215,9 +215,8 @@ class MemoryPool : public std::pmr::memory_resource { * * @param block_size_strategy Memory block growth strategy */ - explicit MemoryPool( - std::unique_ptr block_size_strategy = - std::make_unique()) + explicit MemoryPool(std::unique_ptr block_size_strategy = + std::make_unique()) : block_size_strategy_(std::move(block_size_strategy)) { static_assert(BlockSize >= sizeof(T), "BlockSize must be at least as large as sizeof(T)"); @@ -476,8 +475,7 @@ class MemoryPool : public std::pmr::memory_resource { * @param ptr Pointer to look up * @return The tag associated with the pointer, if any */ - [[nodiscard]] std::optional findTag( - void* ptr) const { + [[nodiscard]] std::optional findTag(void* ptr) const { std::shared_lock lock(mutex_); auto it = tagged_allocations_.find(ptr); if (it != tagged_allocations_.end()) { @@ -491,8 +489,8 @@ class MemoryPool : public std::pmr::memory_resource { * * @return A copy of the pointer-to-tag mapping */ - [[nodiscard]] std::unordered_map - getTaggedAllocations() const { + [[nodiscard]] std::unordered_map getTaggedAllocations() + const { std::shared_lock lock(mutex_); return tagged_allocations_; } @@ -534,8 +532,7 @@ class MemoryPool : public std::pmr::memory_resource { void* ptr = aligned_alloc(alignment, bytes); #endif if (!ptr) { - throw MemoryPoolException( - "Aligned allocation failed"); + throw MemoryPoolException("Aligned allocation failed"); } std::unique_lock lock(mutex_); @@ -744,7 +741,7 @@ class MemoryPool : public std::pmr::memory_resource { std::vector pool_; ///< Pool of memory chunks std::vector free_list_; ///< List of free blocks mutable std::shared_mutex mutex_; ///< Mutex to protect shared resources - MemoryPoolStats stats_; ///< Memory pool statistics + MemoryPoolStats stats_; ///< Memory pool statistics std::unordered_map tagged_allocations_; ///< Tagged allocations }; diff --git a/atom/memory/memory_pool.hpp b/atom/memory/memory_pool.hpp index 3cda2f60..9d864970 100644 --- a/atom/memory/memory_pool.hpp +++ b/atom/memory/memory_pool.hpp @@ -3,15 +3,16 @@ * @brief Fixed-size block memory pool and simple object pool implementations * * This file provides FixedBlockPool and SimpleObjectPool classes for efficient - * fixed-size memory allocations. These are simpler and faster than the variable-size - * MemoryPool when all allocations are the same size. + * fixed-size memory allocations. These are simpler and faster than the + * variable-size MemoryPool when all allocations are the same size. * * CLASSES PROVIDED: * - FixedBlockPool: Low-level fixed-size block allocator * - SimpleObjectPool: Object-oriented wrapper with RAII smart pointers * - PoolPtr: Smart pointer for automatic object return to pool * - * For other memory pool types in atom::memory, see the documentation in memory.hpp. + * For other memory pool types in atom::memory, see the documentation in + * memory.hpp. * * @author Max Qian * @copyright Copyright (C) 2024 Max Qian diff --git a/atom/memory/object.hpp b/atom/memory/object.hpp index 7e3520f9..47787aaf 100644 --- a/atom/memory/object.hpp +++ b/atom/memory/object.hpp @@ -17,12 +17,14 @@ * - Thread-safe with shared_mutex * * COMPARISON WITH OTHER POOLS: - * - Use ObjectPool when you need advanced features like priorities, validation, statistics + * - Use ObjectPool when you need advanced features like priorities, validation, + * statistics * - Use SimpleObjectPool (memory_pool.hpp) for simpler object pooling with RAII * - Use FixedBlockPool (memory_pool.hpp) for low-level fixed-size allocations * - Use MemoryPool (memory.hpp) for variable-size allocations with PMR support * - * For a complete overview of memory pool types, see documentation in memory.hpp. + * For a complete overview of memory pool types, see documentation in + * memory.hpp. * * @author Max Qian * @copyright Copyright (C) 2023-2024 Max Qian diff --git a/atom/memory/shared.hpp b/atom/memory/shared.hpp index c7966347..06c8ef0a 100644 --- a/atom/memory/shared.hpp +++ b/atom/memory/shared.hpp @@ -9,8 +9,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -40,8 +40,9 @@ namespace atom::memory { // Import concepts and types from other namespaces -template -concept TriviallyCopyable = std::is_trivially_copyable_v && std::is_standard_layout_v; +template +concept TriviallyCopyable = + std::is_trivially_copyable_v && std::is_standard_layout_v; /** * @brief Exception class for shared memory errors. @@ -76,7 +77,10 @@ class SharedMemoryException : public std::runtime_error { SharedMemoryException(const char* file, int line, const char* func, const std::string& message, ErrorCode code) : std::runtime_error(formatMessage(file, line, func, message, code)), - code_(code), file_(file), line_(line), func_(func) {} + code_(code), + file_(file), + line_(line), + func_(func) {} /** * @brief Gets the specific error code. @@ -115,7 +119,8 @@ class SharedMemoryException : public std::runtime_error { private: static auto formatMessage(const char* file, int line, const char* func, - const std::string& message, ErrorCode code) -> std::string { + const std::string& message, + ErrorCode code) -> std::string { std::ostringstream oss; oss << "SharedMemoryException occurred:\n"; oss << " File: " << file << "\n"; @@ -158,24 +163,25 @@ class SharedMemoryException : public std::runtime_error { const char* func_{nullptr}; }; -#define THROW_SHARED_MEMORY_ERROR_WITH_CODE(message, code) \ +#define THROW_SHARED_MEMORY_ERROR_WITH_CODE(message, code) \ throw atom::memory::SharedMemoryException(ATOM_FILE_NAME, ATOM_FILE_LINE, \ ATOM_FUNC_NAME, message, code) -#define THROW_SHARED_MEMORY_ERROR(message) \ - throw atom::memory::SharedMemoryException(ATOM_FILE_NAME, ATOM_FILE_LINE, \ - ATOM_FUNC_NAME, message, \ - atom::memory::SharedMemoryException::ErrorCode::UNKNOWN) +#define THROW_SHARED_MEMORY_ERROR(message) \ + throw atom::memory::SharedMemoryException( \ + ATOM_FILE_NAME, ATOM_FILE_LINE, ATOM_FUNC_NAME, message, \ + atom::memory::SharedMemoryException::ErrorCode::UNKNOWN) -#define THROW_NESTED_SHARED_MEMORY_ERROR(message) \ - std::throw_with_nested(atom::memory::SharedMemoryException(ATOM_FILE_NAME, ATOM_FILE_LINE, \ - ATOM_FUNC_NAME, message, \ - atom::memory::SharedMemoryException::ErrorCode::UNKNOWN)) +#define THROW_NESTED_SHARED_MEMORY_ERROR(message) \ + std::throw_with_nested(atom::memory::SharedMemoryException( \ + ATOM_FILE_NAME, ATOM_FILE_LINE, ATOM_FUNC_NAME, message, \ + atom::memory::SharedMemoryException::ErrorCode::UNKNOWN)) /** * @brief Stream operator for SharedMemoryException::ErrorCode */ -inline std::ostream& operator<<(std::ostream& os, const SharedMemoryException::ErrorCode& code) { +inline std::ostream& operator<<(std::ostream& os, + const SharedMemoryException::ErrorCode& code) { switch (code) { case SharedMemoryException::ErrorCode::CREATION_FAILED: return os << "CREATION_FAILED"; @@ -328,9 +334,8 @@ class SharedMemory { */ template ATOM_NODISCARD auto readPartial( - std::size_t offset, - std::chrono::milliseconds timeout = std::chrono::milliseconds(0)) const - -> U; + std::size_t offset, std::chrono::milliseconds timeout = + std::chrono::milliseconds(0)) const -> U; /** * @brief Tries to read data from shared memory without throwing exceptions. @@ -400,9 +405,9 @@ class SharedMemory { * @param timeout The operation timeout. * @return A future indicating the completion of the operation. */ - auto writeAsync(const T& data, std::chrono::milliseconds timeout = - std::chrono::milliseconds(0)) - -> std::future; + auto writeAsync(const T& data, + std::chrono::milliseconds timeout = + std::chrono::milliseconds(0)) -> std::future; /** * @brief Registers a change callback. @@ -505,7 +510,9 @@ SharedMemory::SharedMemory(std::string_view name, bool create, std::memcpy(getDataPtr(), &(*initialData), sizeof(T)); header_->initialized.store(true, std::memory_order_release); header_->version.fetch_add(1, std::memory_order_release); - spdlog::info("Initialized shared memory with initial data: " + name_); + spdlog::info( + "Initialized shared memory with initial data: " + + name_); }, std::chrono::milliseconds(100)); } @@ -535,13 +542,15 @@ void SharedMemory::platformSpecificInit() { std::string eventName = name_ + "_event"; changeEvent_ = CreateEventA(nullptr, TRUE, FALSE, eventName.c_str()); if (!changeEvent_) { - spdlog::warn("Failed to create change event for shared memory: " + getLastErrorMessage()); + spdlog::warn("Failed to create change event for shared memory: " + + getLastErrorMessage()); } #else std::string semName = "/" + name_ + "_sem"; semId_ = sem_open(semName.c_str(), O_CREAT, 0666, 0); if (semId_ == SEM_FAILED) { - spdlog::warn("Failed to create semaphore for shared memory: " + std::string(strerror(errno))); + spdlog::warn("Failed to create semaphore for shared memory: " + + std::string(strerror(errno))); } #endif } @@ -814,9 +823,8 @@ ATOM_NODISCARD bool SharedMemory::exists(std::string_view name) { template template -auto SharedMemory::withLock(Func&& func, - std::chrono::milliseconds timeout) const - -> decltype(std::forward(func)()) { +auto SharedMemory::withLock(Func&& func, std::chrono::milliseconds timeout) + const -> decltype(std::forward(func)()) { std::unique_lock lock(mutex_); auto startTime = std::chrono::steady_clock::now(); @@ -995,9 +1003,8 @@ void SharedMemory::writePartial(const U& data, std::size_t offset, template template -auto SharedMemory::readPartial(std::size_t offset, - std::chrono::milliseconds timeout) const - -> U { +auto SharedMemory::readPartial( + std::size_t offset, std::chrono::milliseconds timeout) const -> U { static_assert(std::is_trivially_copyable_v, "U must be trivially copyable"); @@ -1103,9 +1110,8 @@ auto SharedMemory::readAsync(std::chrono::milliseconds timeout) } template -auto SharedMemory::writeAsync(const T& data, - std::chrono::milliseconds timeout) - -> std::future { +auto SharedMemory::writeAsync( + const T& data, std::chrono::milliseconds timeout) -> std::future { return std::async(std::launch::async, [this, data, timeout]() { this->write(data, timeout); }); } @@ -1180,8 +1186,10 @@ auto SharedMemory::waitForChange(std::chrono::milliseconds timeout) -> bool { template void SharedMemory::startWatchThread() { - watchThread_ = std::jthread( - [this]([[maybe_unused]] std::stop_token stoken) { this->watchForChanges(); }); + watchThread_ = + std::jthread([this]([[maybe_unused]] std::stop_token stoken) { + this->watchForChanges(); + }); } template @@ -1282,7 +1290,7 @@ auto SharedMemory::getNativeHandle() const -> void* { // Backward compatibility alias namespace atom::connection { -template +template using SharedMemory = atom::memory::SharedMemory; using SharedMemoryException = atom::memory::SharedMemoryException; } // namespace atom::connection diff --git a/atom/memory/tracker.cpp b/atom/memory/tracker.cpp index 346b55fe..97908106 100644 --- a/atom/memory/tracker.cpp +++ b/atom/memory/tracker.cpp @@ -1,6 +1,7 @@ /** * @file tracker.cpp - * @brief Implementation of global operator new/delete overloads for memory tracking + * @brief Implementation of global operator new/delete overloads for memory + * tracking * * This file contains the definitions of global operator new/delete overloads * that are used when ATOM_MEMORY_TRACKING_ENABLED is defined. These must be @@ -112,4 +113,3 @@ void operator delete[](void* ptr, const std::nothrow_t&) noexcept { } #endif // ATOM_MEMORY_TRACKING_ENABLED - diff --git a/atom/meta/abi.hpp b/atom/meta/abi.hpp index 433fbf36..ec8bb25e 100644 --- a/atom/meta/abi.hpp +++ b/atom/meta/abi.hpp @@ -21,11 +21,11 @@ #include "atom/containers/high_performance.hpp" #ifdef _WIN32 - #ifdef _MSC_VER - #include - #include - #pragma comment(lib, "dbghelp.lib") - #endif +#ifdef _MSC_VER +#include +#include +#pragma comment(lib, "dbghelp.lib") +#endif #else #include #include @@ -261,16 +261,16 @@ class DemangleHelper { std::free); #else // On Windows, demangling is not available with MinGW - std::unique_ptr demangledName( - nullptr, std::free); - status = -1; // Indicate failure + std::unique_ptr demangledName(nullptr, + std::free); + status = -1; // Indicate failure #endif if (status == 0 && demangledName) { demangled = String(demangledName.get()); } else { - // On Windows or when demangling fails, return the original mangled name - // instead of throwing an exception + // On Windows or when demangling fails, return the original mangled + // name instead of throwing an exception demangled = String(mangled_name); } #endif @@ -310,8 +310,8 @@ class DemangleHelper { * \param indent_level Indentation level for visualization * \return A string containing the hierarchical visualization */ - static auto visualizeType(const String& type_name, int indent_level = 0) - -> String { + static auto visualizeType(const String& type_name, + int indent_level = 0) -> String { String indent(indent_level * 4, ' '); String result; @@ -369,8 +369,8 @@ class DemangleHelper { * \param indent_level Indentation level * \return A visualization of the template parameters */ - static auto visualizeTemplateParams(const String& params, int indent_level) - -> String { + static auto visualizeTemplateParams(const String& params, + int indent_level) -> String { String indent(indent_level * 4, ' '); String result; int paramIndex = 0; @@ -425,8 +425,8 @@ class DemangleHelper { * \param indent_level Indentation level * \return A visualization of the function parameters */ - static auto visualizeFunctionParams(const String& params, int indent_level) - -> String { + static auto visualizeFunctionParams(const String& params, + int indent_level) -> String { if (params.empty()) { return String(indent_level * 4, ' ') + " (no parameters)\n"; } diff --git a/atom/meta/any.hpp b/atom/meta/any.hpp index 92c4d01d..fb19404a 100644 --- a/atom/meta/any.hpp +++ b/atom/meta/any.hpp @@ -365,8 +365,8 @@ class BoxedValue { * \param value The value of the attribute. * \return Reference to this BoxedValue. */ - auto setAttr(const std::string& name, const BoxedValue& value) - -> BoxedValue& { + auto setAttr(const std::string& name, + const BoxedValue& value) -> BoxedValue& { std::unique_lock lock(mutex_); if (!data_->attrs) { data_->attrs = std::make_shared< diff --git a/atom/meta/anymeta.hpp b/atom/meta/anymeta.hpp index 2d573463..5262073c 100644 --- a/atom/meta/anymeta.hpp +++ b/atom/meta/anymeta.hpp @@ -288,8 +288,8 @@ inline auto callMethod(BoxedValue& obj, const std::string& method_name, * \return Property value * \throws atom::error::NotFound if property not found */ -inline auto getProperty(const BoxedValue& obj, const std::string& property_name) - -> BoxedValue { +inline auto getProperty(const BoxedValue& obj, + const std::string& property_name) -> BoxedValue { if (auto metadata = TypeRegistry::instance().getMetadata(obj.getTypeInfo().name())) { if (auto property = metadata->getProperty(property_name)) { diff --git a/atom/meta/awaitable.hpp b/atom/meta/awaitable.hpp index fb9d05af..bfee5b39 100644 --- a/atom/meta/awaitable.hpp +++ b/atom/meta/awaitable.hpp @@ -30,9 +30,7 @@ class SimpleAwaitable { handle.resume(); } - result_type await_resume() { - return std::apply(func_, args_); - } + result_type await_resume() { return std::apply(func_, args_); } private: Function func_; diff --git a/atom/meta/constructor.hpp b/atom/meta/constructor.hpp index f43d9559..ce4b2155 100644 --- a/atom/meta/constructor.hpp +++ b/atom/meta/constructor.hpp @@ -134,7 +134,7 @@ using SafeConstructorResult = ConstructorResult; */ template requires std::is_member_function_pointer_v -auto bindMemberFunction(MemberFunc ClassType::* member_func) { +auto bindMemberFunction(MemberFunc ClassType::*member_func) { return [member_func](ClassType& obj, auto&&... params) -> decltype(auto) { // Use std::invoke for more uniform function calling return std::invoke(member_func, obj, @@ -151,7 +151,7 @@ auto bindMemberFunction(MemberFunc ClassType::* member_func) { */ template requires std::is_member_function_pointer_v -auto bindConstMemberFunction(MemberFunc ClassType::* member_func) { +auto bindConstMemberFunction(MemberFunc ClassType::*member_func) { return [member_func](const ClassType& obj, auto&&... params) -> decltype(auto) { // Always use as const @@ -184,7 +184,7 @@ auto bindStaticFunction(Func&& func) { */ template requires std::is_member_object_pointer_v -auto bindMemberVariable(MemberType ClassType::* member_var) { +auto bindMemberVariable(MemberType ClassType::*member_var) { return [member_var](ClassType& instance) -> MemberType& { return instance.*member_var; }; @@ -199,7 +199,7 @@ auto bindMemberVariable(MemberType ClassType::* member_var) { */ template requires std::is_member_object_pointer_v -auto bindConstMemberVariable(MemberType ClassType::* member_var) { +auto bindConstMemberVariable(MemberType ClassType::*member_var) { return [member_var](const ClassType& instance) -> const MemberType& { return instance.*member_var; }; @@ -556,7 +556,7 @@ class ObjectBuilder { ObjectBuilder() : m_buildFunc([]() { return std::make_shared(); }) {} template - ObjectBuilder& with(Prop Class::* prop, Value&& value) { + ObjectBuilder& with(Prop Class::*prop, Value&& value) { auto prevFunc = m_buildFunc; m_buildFunc = [prevFunc, prop, value = std::forward(value)]() { auto obj = prevFunc(); @@ -567,7 +567,7 @@ class ObjectBuilder { } template - ObjectBuilder& call(Func Class::* method, Args&&... args) { + ObjectBuilder& call(Func Class::*method, Args&&... args) { auto prevFunc = m_buildFunc; m_buildFunc = [prevFunc, method, args = std::make_tuple(std::forward(args)...)]() { diff --git a/atom/meta/container_traits.hpp b/atom/meta/container_traits.hpp index 8ce70f5c..27953add 100644 --- a/atom/meta/container_traits.hpp +++ b/atom/meta/container_traits.hpp @@ -752,8 +752,9 @@ class container_pipe { * \return New container with transformed elements */ template - auto transform(Func func) -> container_pipe>> { + auto transform(Func func) + -> container_pipe>> { std::vector> result; if constexpr (has_reserve_v) { diff --git a/atom/meta/facade_any.hpp b/atom/meta/facade_any.hpp index a08e95c3..a265cc25 100644 --- a/atom/meta/facade_any.hpp +++ b/atom/meta/facade_any.hpp @@ -789,8 +789,8 @@ auto enhancedVar(T&& value) -> EnhancedBoxedValue { * \return EnhancedBoxedValue containing the value and description */ template -auto enhancedVarWithDesc(T&& value, std::string_view description) - -> EnhancedBoxedValue { +auto enhancedVarWithDesc(T&& value, + std::string_view description) -> EnhancedBoxedValue { return EnhancedBoxedValue(std::forward(value), description); } diff --git a/atom/meta/ffi.hpp b/atom/meta/ffi.hpp index c4d7b2dd..461f010d 100644 --- a/atom/meta/ffi.hpp +++ b/atom/meta/ffi.hpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -25,7 +26,6 @@ #include #include #include -#include #if defined(_WIN32) #include @@ -321,8 +321,8 @@ class FFIWrapper { * \param args Function arguments * \return Result or error */ - [[nodiscard]] auto call(void* funcPtr, Args... args) const - -> FFIResult { + [[nodiscard]] auto call(void* funcPtr, + Args... args) const -> FFIResult { if (validate_ && !validateArguments(args...)) { return type::unexpected(FFIError::InvalidArgument); } @@ -347,10 +347,9 @@ class FFIWrapper { * \param args Function arguments * \return Result or error (including timeout) */ - [[nodiscard]] auto callWithTimeout(void* funcPtr, - std::chrono::milliseconds timeout, - Args... args) const - -> FFIResult { + [[nodiscard]] auto callWithTimeout( + void* funcPtr, std::chrono::milliseconds timeout, + Args... args) const -> FFIResult { if (validate_ && !validateArguments(args...)) { return type::unexpected(FFIError::InvalidArgument); } @@ -836,10 +835,10 @@ class CallbackRegistry { void registerCallback(std::string_view callbackName, Func&& func) { std::unique_lock lock(mutex_); - // Store the function directly without trying to construct a specific signature - callbackMap_.emplace( - std::string(callbackName), - std::any{std::forward(func)}); + // Store the function directly without trying to construct a specific + // signature + callbackMap_.emplace(std::string(callbackName), + std::any{std::forward(func)}); } /** @@ -875,14 +874,14 @@ class CallbackRegistry { void registerAsyncCallback(std::string_view callbackName, Func&& func) { std::unique_lock lock(mutex_); - // Store the async wrapper directly without trying to construct a specific signature + // Store the async wrapper directly without trying to construct a + // specific signature auto asyncWrapper = [func = std::forward(func)](auto&&... args) { - return std::async(std::launch::async, func, std::forward(args)...); + return std::async(std::launch::async, func, + std::forward(args)...); }; - callbackMap_.emplace( - std::string(callbackName), - std::any{asyncWrapper}); + callbackMap_.emplace(std::string(callbackName), std::any{asyncWrapper}); } /** diff --git a/atom/meta/func_traits.hpp b/atom/meta/func_traits.hpp index 12eaaae8..4355942d 100644 --- a/atom/meta/func_traits.hpp +++ b/atom/meta/func_traits.hpp @@ -93,7 +93,8 @@ struct FunctionTraits // Variadic function pointer types template -struct FunctionTraits : FunctionTraitsBase { +struct FunctionTraits + : FunctionTraitsBase { static constexpr bool is_variadic = true; }; @@ -270,7 +271,6 @@ struct FunctionTraits static constexpr bool is_noexcept = true; }; - // noexcept qualified rvalue reference and const volatile variants template struct FunctionTraits @@ -502,10 +502,10 @@ class function_pipe { */ template function_pipe(T) -> function_pipe::return_type( - typename std::tuple_element< - 0, typename FunctionTraits::argument_types>::type, - typename std::tuple_element< - 1, typename FunctionTraits::argument_types>::type)>; + typename std::tuple_element< + 0, typename FunctionTraits::argument_types>::type, + typename std::tuple_element< + 1, typename FunctionTraits::argument_types>::type)>; /** * \brief Primary template to detect non-static member function diff --git a/atom/meta/global_ptr.hpp b/atom/meta/global_ptr.hpp index 6bf0269d..53cd0eab 100644 --- a/atom/meta/global_ptr.hpp +++ b/atom/meta/global_ptr.hpp @@ -138,8 +138,8 @@ class GlobalSharedPtrManager : public NonCopyable { * @return Created or retrieved shared pointer */ template - auto getOrCreateSharedPtr(std::string_view key, CreatorFunc creator) - -> std::shared_ptr; + auto getOrCreateSharedPtr(std::string_view key, + CreatorFunc creator) -> std::shared_ptr; /** * @brief Get weak pointer by key @@ -271,9 +271,8 @@ auto GlobalSharedPtrManager::getSharedPtr(std::string_view key) } template -auto GlobalSharedPtrManager::getOrCreateSharedPtr(std::string_view key, - CreatorFunc creator) - -> std::shared_ptr { +auto GlobalSharedPtrManager::getOrCreateSharedPtr( + std::string_view key, CreatorFunc creator) -> std::shared_ptr { const std::string str_key{key}; std::unique_lock lock(mutex_); diff --git a/atom/meta/god.hpp b/atom/meta/god.hpp index b8a416d9..9ecc937a 100644 --- a/atom/meta/god.hpp +++ b/atom/meta/god.hpp @@ -144,9 +144,8 @@ template * \return The aligned value */ template -[[nodiscard]] constexpr auto alignUp(ValueType value, - AlignmentType alignment) noexcept - -> ValueType { +[[nodiscard]] constexpr auto alignUp( + ValueType value, AlignmentType alignment) noexcept -> ValueType { assert((alignment & (alignment - 1)) == 0 && "Alignment must be power of 2"); return (value + static_cast(alignment - 1)) & @@ -162,9 +161,8 @@ template * \return The aligned pointer */ template -[[nodiscard]] constexpr auto alignUp(PointerType* pointer, - AlignmentType alignment) noexcept - -> PointerType* { +[[nodiscard]] constexpr auto alignUp( + PointerType* pointer, AlignmentType alignment) noexcept -> PointerType* { return reinterpret_cast( alignUp(reinterpret_cast(pointer), alignment)); } @@ -206,9 +204,8 @@ template * \return The aligned value */ template -[[nodiscard]] constexpr auto alignDown(ValueType value, - AlignmentType alignment) noexcept - -> ValueType { +[[nodiscard]] constexpr auto alignDown( + ValueType value, AlignmentType alignment) noexcept -> ValueType { assert((alignment & (alignment - 1)) == 0 && "Alignment must be power of 2"); return value & ~static_cast(alignment - 1); @@ -223,9 +220,8 @@ template * \return The aligned pointer */ template -[[nodiscard]] constexpr auto alignDown(PointerType* pointer, - AlignmentType alignment) noexcept - -> PointerType* { +[[nodiscard]] constexpr auto alignDown( + PointerType* pointer, AlignmentType alignment) noexcept -> PointerType* { return reinterpret_cast( alignDown(reinterpret_cast(pointer), alignment)); } @@ -420,9 +416,8 @@ template * \return The original value pointed to by pointer */ template -[[nodiscard]] ATOM_INLINE auto fetchAdd(PointerType* pointer, - ValueType value) noexcept - -> PointerType { +[[nodiscard]] ATOM_INLINE auto fetchAdd( + PointerType* pointer, ValueType value) noexcept -> PointerType { PointerType originalValue = *pointer; *pointer += value; return originalValue; @@ -452,9 +447,8 @@ atomicFetchAdd(std::atomic* pointer, T value, * \return The original value pointed to by pointer */ template -[[nodiscard]] ATOM_INLINE auto fetchSub(PointerType* pointer, - ValueType value) noexcept - -> PointerType { +[[nodiscard]] ATOM_INLINE auto fetchSub( + PointerType* pointer, ValueType value) noexcept -> PointerType { PointerType originalValue = *pointer; *pointer -= value; return originalValue; @@ -485,9 +479,8 @@ atomicFetchSub(std::atomic* pointer, T value, */ template requires BitwiseOperatable -[[nodiscard]] ATOM_INLINE auto fetchAnd(PointerType* pointer, - ValueType value) noexcept - -> PointerType { +[[nodiscard]] ATOM_INLINE auto fetchAnd( + PointerType* pointer, ValueType value) noexcept -> PointerType { PointerType originalValue = *pointer; *pointer &= static_cast(value); return originalValue; @@ -519,9 +512,8 @@ atomicFetchAnd(std::atomic* pointer, T value, */ template requires BitwiseOperatable -[[nodiscard]] ATOM_INLINE auto fetchOr(PointerType* pointer, - ValueType value) noexcept - -> PointerType { +[[nodiscard]] ATOM_INLINE auto fetchOr( + PointerType* pointer, ValueType value) noexcept -> PointerType { PointerType originalValue = *pointer; *pointer |= static_cast(value); return originalValue; @@ -553,9 +545,8 @@ atomicFetchOr(std::atomic* pointer, T value, */ template requires BitwiseOperatable -[[nodiscard]] ATOM_INLINE auto fetchXor(PointerType* pointer, - ValueType value) noexcept - -> PointerType { +[[nodiscard]] ATOM_INLINE auto fetchXor( + PointerType* pointer, ValueType value) noexcept -> PointerType { PointerType originalValue = *pointer; *pointer ^= static_cast(value); return originalValue; diff --git a/atom/meta/invoke.hpp b/atom/meta/invoke.hpp index a211b1f5..14e717a3 100644 --- a/atom/meta/invoke.hpp +++ b/atom/meta/invoke.hpp @@ -245,7 +245,7 @@ template * \return Callable that returns member variable reference */ template -[[nodiscard]] constexpr auto delayMemberVarInvoke(M T::* memberVar, T* obj) { +[[nodiscard]] constexpr auto delayMemberVarInvoke(M T::*memberVar, T* obj) { return [memberVar, obj]() -> M& { if (obj == nullptr) [[unlikely]] { THROW_INVALID_ARGUMENT( diff --git a/atom/meta/member.hpp b/atom/meta/member.hpp index 37f6b1a6..b41b4a97 100644 --- a/atom/meta/member.hpp +++ b/atom/meta/member.hpp @@ -41,7 +41,7 @@ concept member_pointer = std::is_member_pointer_v; * @brief Gets the offset of a member within a structure */ template -consteval std::size_t member_offset(M T::* member) noexcept { +consteval std::size_t member_offset(M T::*member) noexcept { return static_cast(reinterpret_cast( &(static_cast(nullptr)->*member))); } @@ -50,7 +50,7 @@ consteval std::size_t member_offset(M T::* member) noexcept { * @brief Gets the size of a member within a structure */ template -consteval std::size_t member_size(M T::* member) noexcept { +consteval std::size_t member_size(M T::*member) noexcept { return sizeof((static_cast(nullptr)->*member)); } @@ -66,8 +66,7 @@ consteval std::size_t struct_size() noexcept { * @brief Gets the alignment of a member within a structure */ template -consteval std::size_t member_alignment( - [[maybe_unused]] M T::* member) noexcept { +consteval std::size_t member_alignment([[maybe_unused]] M T::*member) noexcept { return alignof(M); } @@ -92,7 +91,7 @@ void print_member_info(const std::string& struct_name, * @brief Validates that a member pointer is not null */ template -constexpr void validate_member_ptr(M T::* member_ptr, +constexpr void validate_member_ptr(M T::*member_ptr, std::string_view operation) { if (member_ptr == nullptr) { throw member_pointer_error( @@ -116,7 +115,7 @@ constexpr void validate_pointer(const T* ptr, std::string_view operation) { * @throws member_pointer_error if member_ptr is null */ template -constexpr std::size_t offset_of(MemberType T::* member_ptr) { +constexpr std::size_t offset_of(MemberType T::*member_ptr) { validate_member_ptr(member_ptr, "offset_of"); return static_cast(reinterpret_cast( &(static_cast(nullptr)->*member_ptr))); @@ -128,7 +127,7 @@ constexpr std::size_t offset_of(MemberType T::* member_ptr) { */ template type::expected safe_container_of( - MemberType* ptr, MemberType Container::* member_ptr) noexcept { + MemberType* ptr, MemberType Container::*member_ptr) noexcept { try { if (ptr == nullptr) { return type::unexpected( @@ -155,7 +154,7 @@ type::expected safe_container_of( * @throws member_pointer_error if validation fails */ template -T* pointer_to_object(MemberType T::* member_ptr, +T* pointer_to_object(MemberType T::*member_ptr, MemberType* member_ptr_address) { static_assert(std::is_member_pointer_v, "member_ptr must be a member pointer"); @@ -174,7 +173,7 @@ T* pointer_to_object(MemberType T::* member_ptr, * @throws member_pointer_error if validation fails */ template -const T* pointer_to_object(MemberType T::* member_ptr, +const T* pointer_to_object(MemberType T::*member_ptr, const MemberType* member_ptr_address) { static_assert(std::is_member_pointer_v, "member_ptr must be a member pointer"); @@ -193,7 +192,7 @@ const T* pointer_to_object(MemberType T::* member_ptr, * @throws member_pointer_error if validation fails */ template -Container* container_of(T* ptr, MemberPtr Container::* member_ptr) { +Container* container_of(T* ptr, MemberPtr Container::*member_ptr) { validate_pointer(ptr, "container_of"); validate_member_ptr(member_ptr, "container_of"); @@ -208,7 +207,7 @@ Container* container_of(T* ptr, MemberPtr Container::* member_ptr) { * @throws member_pointer_error if validation fails */ template -const Container* container_of(const T* ptr, MemberPtr Container::* member_ptr) { +const Container* container_of(const T* ptr, MemberPtr Container::*member_ptr) { validate_pointer(ptr, "container_of"); validate_member_ptr(member_ptr, "container_of"); @@ -263,7 +262,7 @@ auto container_of_if_range(Container& container, Predicate pred) * @brief Check if a pointer points to a member of a specific object */ template -bool is_member_of(const T* obj, const M* member_ptr, M T::* member) noexcept { +bool is_member_of(const T* obj, const M* member_ptr, M T::*member) noexcept { try { validate_pointer(obj, "is_member_of"); validate_pointer(member_ptr, "is_member_of"); diff --git a/atom/meta/property.hpp b/atom/meta/property.hpp index 525242fd..d75d2769 100644 --- a/atom/meta/property.hpp +++ b/atom/meta/property.hpp @@ -257,8 +257,8 @@ class Property { * @param prop The Property object to output. * @return std::ostream& The output stream. */ - friend auto operator<<(std::ostream& outputStream, const Property& prop) - -> std::ostream& { + friend auto operator<<(std::ostream& outputStream, + const Property& prop) -> std::ostream& { try { outputStream << static_cast(prop); } catch (const std::exception&) { diff --git a/atom/meta/refl.hpp b/atom/meta/refl.hpp index 9bd93926..038a649a 100644 --- a/atom/meta/refl.hpp +++ b/atom/meta/refl.hpp @@ -95,8 +95,8 @@ constexpr auto FindIf(const L&, F&&, std::index_sequence<>) -> std::size_t { } template -constexpr auto FindIf(const L& list, F&& func, std::index_sequence) - -> std::size_t { +constexpr auto FindIf(const L& list, F&& func, + std::index_sequence) -> std::size_t { return func(list.template Get()) ? N0 : FindIf(list, std::forward(func), std::index_sequence{}); diff --git a/atom/meta/refl_yaml.hpp b/atom/meta/refl_yaml.hpp index 561ac3ef..538da384 100644 --- a/atom/meta/refl_yaml.hpp +++ b/atom/meta/refl_yaml.hpp @@ -15,13 +15,13 @@ namespace atom::meta { template struct Field { const char* name; - MemberType T::* member; + MemberType T::*member; bool required; MemberType default_value; using Validator = std::function; Validator validator; - Field(const char* n, MemberType T::* m, bool r = true, MemberType def = {}, + Field(const char* n, MemberType T::*m, bool r = true, MemberType def = {}, Validator v = nullptr) : name(n), member(m), @@ -82,7 +82,7 @@ struct Reflectable { // Field creation function template -auto make_field(const char* name, MemberType T::* member, bool required = true, +auto make_field(const char* name, MemberType T::*member, bool required = true, MemberType default_value = {}, typename Field::Validator validator = nullptr) -> Field { diff --git a/atom/meta/signature.hpp b/atom/meta/signature.hpp index e12d3883..2e35e7d4 100644 --- a/atom/meta/signature.hpp +++ b/atom/meta/signature.hpp @@ -129,11 +129,14 @@ class FunctionSignature { bool isExplicit = false) noexcept : name_(name), parameters_(parameters.begin(), parameters.end()), - returnType_(returnType ? std::make_optional(std::string(*returnType)) : std::nullopt), + returnType_(returnType ? std::make_optional(std::string(*returnType)) + : std::nullopt), modifiers_(modifiers), docComment_(docComment), isTemplated_(isTemplated), - templateParams_(templateParams ? std::make_optional(std::string(*templateParams)) : std::nullopt), + templateParams_(templateParams + ? std::make_optional(std::string(*templateParams)) + : std::nullopt), isInline_(isInline), isStatic_(isStatic), isExplicit_(isExplicit) {} @@ -334,7 +337,8 @@ class FunctionSignature { if (tagEnd == std::string_view::npos) break; - std::string tagName = std::string(comment.substr(tagStart, tagEnd - tagStart)); + std::string tagName = + std::string(comment.substr(tagStart, tagEnd - tagStart)); size_t valueStart = comment.find_first_not_of(" \t\n\r", tagEnd); if (valueStart == std::string_view::npos) @@ -358,8 +362,10 @@ class FunctionSignature { } tagValue = atom::utils::trim(tagValue); - // For param tags, store only the first one found (for backward compatibility) - if (tagName == "param" && result.tags.find("param") == result.tags.end()) { + // For param tags, store only the first one found (for backward + // compatibility) + if (tagName == "param" && + result.tags.find("param") == result.tags.end()) { result.tags[tagName] = tagValue; } else if (tagName != "param") { result.tags[tagName] = tagValue; @@ -399,13 +405,17 @@ class FunctionSignature { // Check for specifiers that come before 'def' bool isInline = definition.find(INLINE_MODIFIER) == 0 || - definition.find(" " + std::string(INLINE_MODIFIER)) != std::string_view::npos; + definition.find(" " + std::string(INLINE_MODIFIER)) != + std::string_view::npos; bool isStatic = definition.find(STATIC_MODIFIER) == 0 || - definition.find(" " + std::string(STATIC_MODIFIER)) != std::string_view::npos; + definition.find(" " + std::string(STATIC_MODIFIER)) != + std::string_view::npos; bool isExplicit = definition.find(EXPLICIT_MODIFIER) == 0 || - definition.find(" " + std::string(EXPLICIT_MODIFIER)) != std::string_view::npos; + definition.find(" " + std::string(EXPLICIT_MODIFIER)) != + std::string_view::npos; bool isVirtual = definition.find(VIRTUAL_MODIFIER) == 0 || - definition.find(" " + std::string(VIRTUAL_MODIFIER)) != std::string_view::npos; + definition.find(" " + std::string(VIRTUAL_MODIFIER)) != + std::string_view::npos; if (definition.find(TEMPLATE_PREFIX) == 0) { isTemplated = true; @@ -517,15 +527,15 @@ class FunctionSignature { ++braceCount; else if (c == '}') { if (braceCount == 0) { - return type::unexpected( - ParsingError{UnbalancedBrackets, - "Unbalanced braces in parameters", - paramsStart + i}); + return type::unexpected(ParsingError{ + UnbalancedBrackets, "Unbalanced braces in parameters", + paramsStart + i}); } --braceCount; } - if (c == ',' && bracketCount == 0 && angleCount == 0 && braceCount == 0) { + if (c == ',' && bracketCount == 0 && angleCount == 0 && + braceCount == 0) { paramEnd = i; break; } @@ -568,12 +578,13 @@ class FunctionSignature { squareBracketDepth++; } else if (c == ']') { squareBracketDepth--; - } else if (c == '=' && braceDepth == 0 && squareBracketDepth == 0) { + } else if (c == '=' && braceDepth == 0 && + squareBracketDepth == 0) { equalsPos = i; break; } } else { - if (c == quoteChar && (i == 0 || param[i-1] != '\\')) { + if (c == quoteChar && (i == 0 || param[i - 1] != '\\')) { inQuotes = false; quoteChar = '\0'; } diff --git a/atom/meta/type_caster.hpp b/atom/meta/type_caster.hpp index daa80fb0..28d24fa2 100644 --- a/atom/meta/type_caster.hpp +++ b/atom/meta/type_caster.hpp @@ -255,8 +255,8 @@ class TypeCaster { * \throws std::invalid_argument if the enum value is invalid. */ template - auto enumToString(EnumType value, const std::string& enum_name) - -> std::string { + auto enumToString(EnumType value, + const std::string& enum_name) -> std::string { std::shared_lock enumLock(enum_mutex_); const auto& enumMap = getEnumMap(enum_name); for (const auto& [key, enumValue] : enumMap) { diff --git a/atom/meta/type_info.hpp b/atom/meta/type_info.hpp index 0a4cd9f5..0947b1aa 100644 --- a/atom/meta/type_info.hpp +++ b/atom/meta/type_info.hpp @@ -148,7 +148,8 @@ class TypeInfo { flags.set(IS_UNBOUNDED_ARRAY_FLAG, std::is_unbounded_array_v); // C++20 compatible scoped enum detection if constexpr (std::is_enum_v) { - flags.set(IS_SCOPED_ENUM_FLAG, !std::is_convertible_v>); + flags.set(IS_SCOPED_ENUM_FLAG, + !std::is_convertible_v>); } else { flags.set(IS_SCOPED_ENUM_FLAG, false); } @@ -167,8 +168,8 @@ class TypeInfo { * @return TypeInfo object containing information about T */ template - static auto fromInstance(const T& instance [[maybe_unused]]) noexcept - -> TypeInfo { + static auto fromInstance(const T& instance + [[maybe_unused]]) noexcept -> TypeInfo { return fromType(); } @@ -681,8 +682,8 @@ class TypeFactory { } // namespace atom::meta -inline auto operator<<(std::ostream& oss, const atom::meta::TypeInfo& typeInfo) - -> std::ostream& { +inline auto operator<<(std::ostream& oss, + const atom::meta::TypeInfo& typeInfo) -> std::ostream& { return oss << typeInfo.name(); } diff --git a/atom/search/CMakeLists.txt b/atom/search/CMakeLists.txt index fc4a17f2..64703c34 100644 --- a/atom/search/CMakeLists.txt +++ b/atom/search/CMakeLists.txt @@ -1,5 +1,8 @@ cmake_minimum_required(VERSION 3.21) -project(atom-search VERSION 1.0.0 LANGUAGES C CXX) +project( + atom-search + VERSION 1.0.0 + LANGUAGES C CXX) # Include standardized module configuration include(${CMAKE_SOURCE_DIR}/cmake/ModuleDependencies.cmake) @@ -8,10 +11,8 @@ include(${CMAKE_SOURCE_DIR}/cmake/ModuleDependencies.cmake) set(SOURCES # Core search functionality core/search.cpp - # Database backends - database/sqlite.cpp -) + database/sqlite.cpp) # Headers set(HEADERS @@ -22,16 +23,12 @@ set(HEADERS mysql.hpp sqlite.hpp search.hpp - # Actual implementation headers (in subdirectories) core/search.hpp - database/sqlite.hpp - cache/cache.hpp cache/lru.hpp - cache/ttl.hpp -) + cache/ttl.hpp) # Dependencies set(LIBS loguru ${CMAKE_THREAD_LIBS_INIT} ${SQLite3_LIBRARIES}) @@ -58,6 +55,4 @@ atom_configure_module(atom-search) target_link_libraries(atom-search PRIVATE ${LIBS}) # Install headers -install(FILES ${HEADERS} - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/atom/search -) +install(FILES ${HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/atom/search) diff --git a/atom/search/cache/cache.hpp b/atom/search/cache/cache.hpp index 74c2ebc6..5d9115be 100644 --- a/atom/search/cache/cache.hpp +++ b/atom/search/cache/cache.hpp @@ -291,8 +291,8 @@ class ResourceCache { * @param loadDataFunction The function to load the resource. * @return A future that completes when the resource is loaded. */ - auto asyncLoad(const String &key, std::function loadDataFunction) - -> Future; + auto asyncLoad(const String &key, + std::function loadDataFunction) -> Future; /** * @brief Sets the maximum size of the cache. @@ -463,7 +463,7 @@ auto ResourceCache::contains(const String &key) const -> bool { if ((now - it->second.second) >= expIt->second) { // Expired: release shared lock and remove the key lock.unlock(); - const_cast*>(this)->remove(key); + const_cast *>(this)->remove(key); return false; } } @@ -643,9 +643,8 @@ auto ResourceCache::isExpired(const String &key) const -> bool { } template -auto ResourceCache::asyncLoad(const String &key, - std::function loadDataFunction) - -> Future { +auto ResourceCache::asyncLoad( + const String &key, std::function loadDataFunction) -> Future { return std::async(std::launch::async, [this, key, loadDataFunction]() { try { T value = loadDataFunction(); diff --git a/atom/search/cache/lru.hpp b/atom/search/cache/lru.hpp index a421a842..5cdf77ff 100644 --- a/atom/search/cache/lru.hpp +++ b/atom/search/cache/lru.hpp @@ -525,7 +525,8 @@ auto ThreadSafeLRUCache::getShared(const Key& key) noexcept } hit_count_++; if constexpr (std::is_trivially_copy_constructible_v) { - cache_items_list_.splice(cache_items_list_.begin(), cache_items_list_, + cache_items_list_.splice(cache_items_list_.begin(), + cache_items_list_, iterator->second.iterator); } return iterator->second.value; @@ -686,7 +687,7 @@ void ThreadSafeLRUCache::putBatch( iterator->second.iterator); iterator->second.value = valuePtr; iterator->second.expiryTime = expiryTime; - iterator->second.iterator->second = value; // keep list in sync + iterator->second.iterator->second = value; // keep list in sync } else { cache_items_list_.emplace_front(key, value); cache_items_map_[key] = {valuePtr, expiryTime, @@ -984,15 +985,21 @@ void ThreadSafeLRUCache::saveToFile( } // Collect non-expired items in LRU order - struct ItemRec { Key key; int64_t ttl; Value value; }; + struct ItemRec { + Key key; + int64_t ttl; + Value value; + }; std::vector items; items.reserve(cache_items_map_.size()); auto now = Clock::now(); for (const auto& pair : cache_items_list_) { auto it = cache_items_map_.find(pair.first); - if (it == cache_items_map_.end()) continue; - if (isExpired(it->second)) continue; + if (it == cache_items_map_.end()) + continue; + if (isExpired(it->second)) + continue; int64_t remainingTtl = -1; if (it->second.expiryTime != TimePoint::max()) { @@ -1000,9 +1007,11 @@ void ThreadSafeLRUCache::saveToFile( std::chrono::duration_cast( it->second.expiryTime - now); remainingTtl = ttlDuration.count(); - if (remainingTtl <= 0) continue; + if (remainingTtl <= 0) + continue; } - items.push_back(ItemRec{pair.first, remainingTtl, *(it->second.value)}); + items.push_back( + ItemRec{pair.first, remainingTtl, *(it->second.value)}); } size_t outSize = items.size(); @@ -1013,7 +1022,8 @@ void ThreadSafeLRUCache::saveToFile( if constexpr (std::is_same_v) { size_t len = key.size(); ofs.write(reinterpret_cast(&len), sizeof(len)); - if (len) ofs.write(key.data(), static_cast(len)); + if (len) + ofs.write(key.data(), static_cast(len)); } else { ofs.write(reinterpret_cast(&key), sizeof(Key)); } @@ -1052,7 +1062,8 @@ void ThreadSafeLRUCache::saveToFile( template void ThreadSafeLRUCache::loadFromFile(const std::string& filename) { try { - // Read file contents without holding cache locks to avoid long critical sections + // Read file contents without holding cache locks to avoid long critical + // sections std::ifstream ifs(filename, std::ios::binary); if (!ifs) { throw LRUCacheIOException("Failed to open file for reading: " + @@ -1062,7 +1073,8 @@ void ThreadSafeLRUCache::loadFromFile(const std::string& filename) { size_t itemCount = 0; size_t storedMaxSize = 0; ifs.read(reinterpret_cast(&itemCount), sizeof(itemCount)); - ifs.read(reinterpret_cast(&storedMaxSize), sizeof(storedMaxSize)); + ifs.read(reinterpret_cast(&storedMaxSize), + sizeof(storedMaxSize)); if (!ifs) { throw LRUCacheIOException( "Failed to read cache metadata from file"); @@ -1073,13 +1085,18 @@ void ThreadSafeLRUCache::loadFromFile(const std::string& filename) { size_t len = 0; ifs.read(reinterpret_cast(&len), sizeof(len)); key.resize(len); - if (len) ifs.read(&key[0], static_cast(len)); + if (len) + ifs.read(&key[0], static_cast(len)); } else { ifs.read(reinterpret_cast(&key), sizeof(Key)); } }; - struct InItem { Key key; Value value; std::optional ttl; }; + struct InItem { + Key key; + Value value; + std::optional ttl; + }; std::vector items; items.reserve(itemCount); @@ -1093,10 +1110,12 @@ void ThreadSafeLRUCache::loadFromFile(const std::string& filename) { Value value; if constexpr (std::is_same_v) { size_t valueSize = 0; - ifs.read(reinterpret_cast(&valueSize), sizeof(valueSize)); + ifs.read(reinterpret_cast(&valueSize), + sizeof(valueSize)); value.resize(valueSize); if (valueSize) - ifs.read(&value[0], static_cast(valueSize)); + ifs.read(&value[0], + static_cast(valueSize)); } else { ifs.read(reinterpret_cast(&value), sizeof(value)); } @@ -1114,7 +1133,8 @@ void ThreadSafeLRUCache::loadFromFile(const std::string& filename) { items.push_back(InItem{std::move(key), std::move(value), ttl}); } - // Now update the cache state using public APIs (each acquires its own lock) + // Now update the cache state using public APIs (each acquires its own + // lock) clear(); for (auto& it : items) { put(it.key, std::move(it.value), it.ttl); @@ -1224,8 +1244,8 @@ auto ThreadSafeLRUCache::asyncGet(const Key& key) template auto ThreadSafeLRUCache::asyncPut( - const Key& key, Value value, std::optional ttl) - -> future { + const Key& key, Value value, + std::optional ttl) -> future { return std::async(std::launch::async, [this, key, value = std::move(value), ttl]() mutable { put(key, std::move(value), ttl); diff --git a/atom/search/cache/ttl.hpp b/atom/search/cache/ttl.hpp index 40681825..6787eb1f 100644 --- a/atom/search/cache/ttl.hpp +++ b/atom/search/cache/ttl.hpp @@ -464,11 +464,13 @@ class TTLCache { void cleanup_expired_items(UniqueLock& lock) noexcept; // Helper methods for get operations - template - std::optional get_impl(const Key& key, bool update_access_time, LockType& lock); + template + std::optional get_impl(const Key& key, bool update_access_time, + LockType& lock); - template - ValuePtr get_shared_impl(const Key& key, bool update_access_time, LockType& lock); + template + ValuePtr get_shared_impl(const Key& key, bool update_access_time, + LockType& lock); }; template @@ -1208,7 +1210,7 @@ void TTLCache::cleanup_expired_items( } template -template +template std::optional TTLCache::get_impl( const Key& key, bool update_access_time, LockType& lock) { auto map_it = cache_map_.find(key); @@ -1250,10 +1252,11 @@ std::optional TTLCache::get_impl( } template -template +template typename TTLCache::ValuePtr -TTLCache::get_shared_impl( - const Key& key, bool update_access_time, LockType& lock) { +TTLCache::get_shared_impl(const Key& key, + bool update_access_time, + LockType& lock) { auto map_it = cache_map_.find(key); if (map_it == cache_map_.end()) { if (config_.enable_statistics) { diff --git a/atom/search/database/sqlite.cpp b/atom/search/database/sqlite.cpp index 03ee54df..795d35e0 100644 --- a/atom/search/database/sqlite.cpp +++ b/atom/search/database/sqlite.cpp @@ -862,17 +862,34 @@ template std::optional SqliteDB::getSingleValue( std::string_view query, double (*columnFunc)(sqlite3_stmt*, int)); // Additional explicit template instantiations for test cases -template bool SqliteDB::executeParameterizedQuery(std::string_view, const char*&&, int&&); -template bool SqliteDB::executeParameterizedQuery(std::string_view, const char*&, const char*&, int&&); -template bool SqliteDB::executeParameterizedQuery(std::string_view, int&&, const char*&); -template bool SqliteDB::executeParameterizedQuery(std::string_view, std::string&&, std::string&&, int&&); +template bool SqliteDB::executeParameterizedQuery( + std::string_view, const char*&&, int&&); +template bool SqliteDB::executeParameterizedQuery(std::string_view, + const char*&, + const char*&, int&&); +template bool SqliteDB::executeParameterizedQuery( + std::string_view, int&&, const char*&); +template bool SqliteDB::executeParameterizedQuery(std::string_view, + std::string&&, + std::string&&, int&&); // Final template instantiations for remaining string literal combinations -template bool SqliteDB::executeParameterizedQuery(std::string_view, std::string&, char const (&)[15], int&&); -template bool SqliteDB::executeParameterizedQuery(std::string_view, std::string&, char const (&)[17], int&&); +template bool +SqliteDB::executeParameterizedQuery( + std::string_view, std::string&, char const (&)[15], int&&); +template bool +SqliteDB::executeParameterizedQuery( + std::string_view, std::string&, char const (&)[17], int&&); // selectParameterizedData instantiations (these are new) -template SqliteDB::ResultSet SqliteDB::selectParameterizedData(std::string_view, int&&); -template SqliteDB::ResultSet SqliteDB::selectParameterizedData(std::string_view, std::string&&); -template SqliteDB::ResultSet SqliteDB::selectParameterizedData(std::string_view, const char*&&); -template SqliteDB::ResultSet SqliteDB::selectParameterizedData(std::string_view, const char*&&, int&&); +template SqliteDB::ResultSet SqliteDB::selectParameterizedData( + std::string_view, int&&); +template SqliteDB::ResultSet SqliteDB::selectParameterizedData( + std::string_view, std::string&&); +template SqliteDB::ResultSet SqliteDB::selectParameterizedData( + std::string_view, const char*&&); +template SqliteDB::ResultSet +SqliteDB::selectParameterizedData(std::string_view, + const char*&&, int&&); diff --git a/atom/secret/CMakeLists.txt b/atom/secret/CMakeLists.txt index eda8a420..a446a77d 100644 --- a/atom/secret/CMakeLists.txt +++ b/atom/secret/CMakeLists.txt @@ -1,25 +1,21 @@ -# CMakeLists.txt for Atom-Secret -# This project is licensed under the terms of the GPL3 license. +# CMakeLists.txt for Atom-Secret This project is licensed under the terms of the +# GPL3 license. # -# Project Name: Atom-Secret -# Description: Secret Management Library for Atom -# Author: Max Qian -# License: GPL3 +# Project Name: Atom-Secret Description: Secret Management Library for Atom +# Author: Max Qian License: GPL3 cmake_minimum_required(VERSION 3.21) -project(atom-secret VERSION 1.0.0 LANGUAGES C CXX) +project( + atom-secret + VERSION 1.0.0 + LANGUAGES C CXX) # Include standardized module configuration include(${CMAKE_SOURCE_DIR}/cmake/ModuleDependencies.cmake) # Sources and Headers -set(SOURCES - encryption.cpp - password_manager.cpp - password_utils.cpp - serialization.cpp - storage.cpp -) +set(SOURCES encryption.cpp password_manager.cpp password_utils.cpp + serialization.cpp storage.cpp) set(HEADERS common.hpp @@ -29,27 +25,24 @@ set(HEADERS password_utils.hpp result.hpp serialization.hpp - storage.hpp -) + storage.hpp) -set(LIBS - loguru - ${CMAKE_THREAD_LIBS_INIT} -) +set(LIBS loguru ${CMAKE_THREAD_LIBS_INIT}) # Find required packages find_package(OpenSSL QUIET) if(OpenSSL_FOUND) - list(APPEND LIBS OpenSSL::SSL OpenSSL::Crypto) - message(STATUS "OpenSSL found - cryptographic features enabled") + list(APPEND LIBS OpenSSL::SSL OpenSSL::Crypto) + message(STATUS "OpenSSL found - cryptographic features enabled") else() - message(STATUS "OpenSSL not found - some cryptographic features will be disabled") + message( + STATUS "OpenSSL not found - some cryptographic features will be disabled") endif() # Find spdlog find_package(spdlog QUIET) if(spdlog_FOUND) - list(APPEND LIBS spdlog::spdlog) + list(APPEND LIBS spdlog::spdlog) endif() # Create library target @@ -62,15 +55,15 @@ atom_configure_module(atom-secret) target_link_libraries(atom-secret PRIVATE ${LIBS}) # Platform-specific dependencies -if (LINUX) - find_package(PkgConfig REQUIRED) - pkg_check_modules(GLIB REQUIRED glib-2.0) - pkg_check_modules(LIBSECRET REQUIRED libsecret-1) - target_link_libraries(atom-secret PRIVATE ${GLIB_LIBRARIES} ${LIBSECRET_LIBRARIES}) - target_include_directories(atom-secret PUBLIC ${GLIB_INCLUDE_DIRS} ${LIBSECRET_INCLUDE_DIRS}) +if(LINUX) + find_package(PkgConfig REQUIRED) + pkg_check_modules(GLIB REQUIRED glib-2.0) + pkg_check_modules(LIBSECRET REQUIRED libsecret-1) + target_link_libraries(atom-secret PRIVATE ${GLIB_LIBRARIES} + ${LIBSECRET_LIBRARIES}) + target_include_directories(atom-secret PUBLIC ${GLIB_INCLUDE_DIRS} + ${LIBSECRET_INCLUDE_DIRS}) endif() # Install headers -install(FILES ${HEADERS} - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/atom/secret -) +install(FILES ${HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/atom/secret) diff --git a/atom/secret/encryption.cpp b/atom/secret/encryption.cpp index 49bb9f08..6c87bc22 100644 --- a/atom/secret/encryption.cpp +++ b/atom/secret/encryption.cpp @@ -1,9 +1,9 @@ #include "encryption.hpp" +#include #include #include #include -#include #include #include @@ -65,7 +65,8 @@ void SecureMemory::secureClear(void* ptr, size_t size) noexcept { } // First pass: overwrite with random data - if (RAND_bytes(static_cast(ptr), static_cast(size)) != 1) { + if (RAND_bytes(static_cast(ptr), static_cast(size)) != + 1) { // Fallback to deterministic pattern if random fails std::memset(ptr, 0xAA, size); std::memset(ptr, 0x55, size); @@ -86,7 +87,7 @@ void SecureMemory::secureClear(std::string& str) noexcept { } } -template +template void SecureMemory::secureClear(std::vector& vec) noexcept { if (!vec.empty()) { secureClear(vec.data(), vec.size() * sizeof(T)); @@ -96,7 +97,8 @@ void SecureMemory::secureClear(std::vector& vec) noexcept { } // Explicit template instantiations -template void SecureMemory::secureClear(std::vector&) noexcept; +template void SecureMemory::secureClear( + std::vector&) noexcept; template void SecureMemory::secureClear(std::vector&) noexcept; bool SecureMemory::lockMemory(void* ptr, size_t size) noexcept { @@ -173,13 +175,12 @@ void SecureMemory::freeSecure(void* ptr, size_t size) noexcept { // SecureBuffer Template Implementation // ============================================================================ -template +template SecureBuffer::SecureBuffer(size_t size) : data_(static_cast(SecureMemory::allocateSecure(size * sizeof(T)))), - size_(data_ ? size : 0) { -} + size_(data_ ? size : 0) {} -template +template SecureBuffer::~SecureBuffer() { if (data_) { SecureMemory::freeSecure(data_, size_ * sizeof(T)); @@ -188,14 +189,14 @@ SecureBuffer::~SecureBuffer() { } } -template +template SecureBuffer::SecureBuffer(SecureBuffer&& other) noexcept : data_(other.data_), size_(other.size_) { other.data_ = nullptr; other.size_ = 0; } -template +template SecureBuffer& SecureBuffer::operator=(SecureBuffer&& other) noexcept { if (this != &other) { if (data_) { @@ -218,11 +219,8 @@ template class SecureBuffer; // ============================================================================ Result> KeyDerivation::deriveKey( - std::string_view password, - const std::vector& salt, - int iterations, + std::string_view password, const std::vector& salt, int iterations, size_t keyLength) { - if (password.empty()) { return Result>::error("Password cannot be empty"); } @@ -232,7 +230,8 @@ Result> KeyDerivation::deriveKey( } if (iterations < 1000) { - return Result>::error("Iteration count too low (minimum 1000)"); + return Result>::error( + "Iteration count too low (minimum 1000)"); } if (keyLength == 0 || keyLength > 1024) { @@ -242,12 +241,9 @@ Result> KeyDerivation::deriveKey( std::vector derivedKey(keyLength); int result = PKCS5_PBKDF2_HMAC( - password.data(), static_cast(password.length()), - salt.data(), static_cast(salt.size()), - iterations, - EVP_sha256(), - static_cast(keyLength), - derivedKey.data()); + password.data(), static_cast(password.length()), salt.data(), + static_cast(salt.size()), iterations, EVP_sha256(), + static_cast(keyLength), derivedKey.data()); if (result != 1) { return Result>::error("Key derivation failed"); @@ -264,7 +260,8 @@ Result> KeyDerivation::generateSalt(size_t length) { std::vector salt(length); if (RAND_bytes(salt.data(), static_cast(length)) != 1) { - return Result>::error("Failed to generate random salt"); + return Result>::error( + "Failed to generate random salt"); } return Result>(std::move(salt)); @@ -278,7 +275,8 @@ Result> KeyDerivation::generateKey(size_t length) { std::vector key(length); if (RAND_bytes(key.data(), static_cast(length)) != 1) { - return Result>::error("Failed to generate random key"); + return Result>::error( + "Failed to generate random key"); } return Result>(std::move(key)); @@ -291,7 +289,8 @@ Result> KeyDerivation::generateKey(size_t length) { std::vector EncryptedData::serialize() const { std::vector result; - // Format: [version:1][method:1][iterations:4][salt_len:4][iv_len:4][tag_len:4][cipher_len:4] + // Format: + // [version:1][method:1][iterations:4][salt_len:4][iv_len:4][tag_len:4][cipher_len:4] // [salt][iv][tag][ciphertext] const uint8_t version = 1; @@ -327,8 +326,9 @@ std::vector EncryptedData::serialize() const { return result; } -Result EncryptedData::deserialize(const std::vector& data) { - if (data.size() < 22) { // Minimum header size +Result EncryptedData::deserialize( + const std::vector& data) { + if (data.size() < 22) { // Minimum header size return Result::error("Invalid encrypted data format"); } @@ -337,7 +337,8 @@ Result EncryptedData::deserialize(const std::vector& dat // Read version uint8_t version = data[pos++]; if (version != 1) { - return Result::error("Unsupported encrypted data version"); + return Result::error( + "Unsupported encrypted data version"); } // Read method @@ -346,7 +347,8 @@ Result EncryptedData::deserialize(const std::vector& dat return Result::error("Unknown encryption method"); } - EncryptionOptions::Method method = static_cast(methodByte); + EncryptionOptions::Method method = + static_cast(methodByte); // Read iterations (4 bytes, big-endian) if (pos + 4 > data.size()) { @@ -354,18 +356,19 @@ Result EncryptedData::deserialize(const std::vector& dat } uint32_t iterations = (static_cast(data[pos]) << 24) | - (static_cast(data[pos + 1]) << 16) | - (static_cast(data[pos + 2]) << 8) | - static_cast(data[pos + 3]); + (static_cast(data[pos + 1]) << 16) | + (static_cast(data[pos + 2]) << 8) | + static_cast(data[pos + 3]); pos += 4; // Read lengths auto readLengthBE = [&data, &pos]() -> uint32_t { - if (pos + 4 > data.size()) return 0; + if (pos + 4 > data.size()) + return 0; uint32_t len = (static_cast(data[pos]) << 24) | - (static_cast(data[pos + 1]) << 16) | - (static_cast(data[pos + 2]) << 8) | - static_cast(data[pos + 3]); + (static_cast(data[pos + 1]) << 16) | + (static_cast(data[pos + 2]) << 8) | + static_cast(data[pos + 3]); pos += 4; return len; }; @@ -394,7 +397,8 @@ Result EncryptedData::deserialize(const std::vector& dat result.tag.assign(data.begin() + pos, data.begin() + pos + tagLen); pos += tagLen; - result.ciphertext.assign(data.begin() + pos, data.begin() + pos + cipherLen); + result.ciphertext.assign(data.begin() + pos, + data.begin() + pos + cipherLen); return Result(std::move(result)); } @@ -403,11 +407,9 @@ Result EncryptedData::deserialize(const std::vector& dat // Encryption Implementation // ============================================================================ -Result Encryption::encrypt( - std::string_view plaintext, - std::string_view password, - const EncryptionOptions& options) { - +Result Encryption::encrypt(std::string_view plaintext, + std::string_view password, + const EncryptionOptions& options) { if (plaintext.empty()) { return Result::error("Plaintext cannot be empty"); } @@ -419,15 +421,17 @@ Result Encryption::encrypt( // Generate salt auto saltResult = KeyDerivation::generateSalt(32); if (saltResult.isError()) { - return Result::error("Failed to generate salt: " + saltResult.error()); + return Result::error("Failed to generate salt: " + + saltResult.error()); } // Derive key size_t keySize = getKeySize(options.encryptionMethod); auto keyResult = KeyDerivation::deriveKey(password, saltResult.value(), - options.keyIterations, keySize); + options.keyIterations, keySize); if (keyResult.isError()) { - return Result::error("Failed to derive key: " + keyResult.error()); + return Result::error("Failed to derive key: " + + keyResult.error()); } // Encrypt with derived key @@ -442,15 +446,14 @@ Result Encryption::encrypt( result.keyIterations = options.keyIterations; // Clear sensitive data - SecureMemory::secureClear(const_cast&>(keyResult.value())); + SecureMemory::secureClear( + const_cast&>(keyResult.value())); return Result(std::move(result)); } -Result Encryption::decrypt( - const EncryptedData& encryptedData, - std::string_view password) { - +Result Encryption::decrypt(const EncryptedData& encryptedData, + std::string_view password) { if (password.empty()) { return Result::error("Password cannot be empty"); } @@ -461,26 +464,26 @@ Result Encryption::decrypt( // Derive key size_t keySize = getKeySize(encryptedData.method); - auto keyResult = KeyDerivation::deriveKey(password, encryptedData.salt, - encryptedData.keyIterations, keySize); + auto keyResult = KeyDerivation::deriveKey( + password, encryptedData.salt, encryptedData.keyIterations, keySize); if (keyResult.isError()) { - return Result::error("Failed to derive key: " + keyResult.error()); + return Result::error("Failed to derive key: " + + keyResult.error()); } // Decrypt with derived key auto decryptResult = decryptWithKey(encryptedData, keyResult.value()); // Clear sensitive data - SecureMemory::secureClear(const_cast&>(keyResult.value())); + SecureMemory::secureClear( + const_cast&>(keyResult.value())); return decryptResult; } Result Encryption::encryptWithKey( - std::string_view plaintext, - const std::vector& key, + std::string_view plaintext, const std::vector& key, const EncryptionOptions& options) { - if (plaintext.empty()) { return Result::error("Plaintext cannot be empty"); } @@ -493,7 +496,8 @@ Result Encryption::encryptWithKey( size_t ivSize = getIvSize(options.encryptionMethod); auto ivResult = KeyDerivation::generateKey(ivSize); if (ivResult.isError()) { - return Result::error("Failed to generate IV: " + ivResult.error()); + return Result::error("Failed to generate IV: " + + ivResult.error()); } EncryptedData result; @@ -505,7 +509,8 @@ Result Encryption::encryptWithKey( case EncryptionOptions::Method::AES_GCM: { auto encryptResult = encryptAesGcm(plaintext, key, result.iv); if (encryptResult.isError()) { - return Result::error("AES-GCM encryption failed: " + encryptResult.error()); + return Result::error( + "AES-GCM encryption failed: " + encryptResult.error()); } result.ciphertext = std::move(encryptResult.value().first); result.tag = std::move(encryptResult.value().second); @@ -514,13 +519,15 @@ Result Encryption::encryptWithKey( case EncryptionOptions::Method::AES_CBC: { auto encryptResult = encryptAesCbc(plaintext, key, result.iv); if (encryptResult.isError()) { - return Result::error("AES-CBC encryption failed: " + encryptResult.error()); + return Result::error( + "AES-CBC encryption failed: " + encryptResult.error()); } result.ciphertext = std::move(encryptResult.value()); break; } case EncryptionOptions::Method::CHACHA20_POLY1305: { - return Result::error("ChaCha20-Poly1305 not yet implemented"); + return Result::error( + "ChaCha20-Poly1305 not yet implemented"); } default: return Result::error("Unknown encryption method"); @@ -530,9 +537,7 @@ Result Encryption::encryptWithKey( } Result Encryption::decryptWithKey( - const EncryptedData& encryptedData, - const std::vector& key) { - + const EncryptedData& encryptedData, const std::vector& key) { if (key.empty()) { return Result::error("Key cannot be empty"); } @@ -545,11 +550,13 @@ Result Encryption::decryptWithKey( switch (encryptedData.method) { case EncryptionOptions::Method::AES_GCM: return decryptAesGcm(encryptedData.ciphertext, key, - encryptedData.iv, encryptedData.tag); + encryptedData.iv, encryptedData.tag); case EncryptionOptions::Method::AES_CBC: - return decryptAesCbc(encryptedData.ciphertext, key, encryptedData.iv); + return decryptAesCbc(encryptedData.ciphertext, key, + encryptedData.iv); case EncryptionOptions::Method::CHACHA20_POLY1305: - return Result::error("ChaCha20-Poly1305 not yet implemented"); + return Result::error( + "ChaCha20-Poly1305 not yet implemented"); default: return Result::error("Unknown encryption method"); } @@ -560,92 +567,101 @@ Result Encryption::decryptWithKey( // ============================================================================ Result, std::vector>> -Encryption::encryptAesGcm( - std::string_view plaintext, - const std::vector& key, - const std::vector& iv) { - +Encryption::encryptAesGcm(std::string_view plaintext, + const std::vector& key, + const std::vector& iv) { try { SslCipherContext ctx; // Initialize encryption - if (EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), nullptr, nullptr, nullptr) != 1) { - return Result, std::vector>>::error( - "Failed to initialize AES-GCM encryption"); + if (EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), nullptr, nullptr, + nullptr) != 1) { + return Result< + std::pair, std::vector>>:: + error("Failed to initialize AES-GCM encryption"); } // Set IV length if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, - static_cast(iv.size()), nullptr) != 1) { - return Result, std::vector>>::error( - "Failed to set IV length"); + static_cast(iv.size()), nullptr) != 1) { + return Result< + std::pair, std::vector>>:: + error("Failed to set IV length"); } // Set key and IV - if (EVP_EncryptInit_ex(ctx, nullptr, nullptr, key.data(), iv.data()) != 1) { - return Result, std::vector>>::error( - "Failed to set key and IV"); + if (EVP_EncryptInit_ex(ctx, nullptr, nullptr, key.data(), iv.data()) != + 1) { + return Result< + std::pair, std::vector>>:: + error("Failed to set key and IV"); } // Encrypt - std::vector ciphertext(plaintext.length() + 16); // Extra space for padding + std::vector ciphertext(plaintext.length() + + 16); // Extra space for padding int len = 0; int ciphertext_len = 0; - if (EVP_EncryptUpdate(ctx, ciphertext.data(), &len, - reinterpret_cast(plaintext.data()), - static_cast(plaintext.length())) != 1) { - return Result, std::vector>>::error( - "Failed to encrypt data"); + if (EVP_EncryptUpdate( + ctx, ciphertext.data(), &len, + reinterpret_cast(plaintext.data()), + static_cast(plaintext.length())) != 1) { + return Result< + std::pair, std::vector>>:: + error("Failed to encrypt data"); } ciphertext_len = len; // Finalize encryption if (EVP_EncryptFinal_ex(ctx, ciphertext.data() + len, &len) != 1) { - return Result, std::vector>>::error( - "Failed to finalize encryption"); + return Result< + std::pair, std::vector>>:: + error("Failed to finalize encryption"); } ciphertext_len += len; ciphertext.resize(ciphertext_len); // Get authentication tag std::vector tag(16); - if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, tag.data()) != 1) { - return Result, std::vector>>::error( - "Failed to get authentication tag"); + if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, tag.data()) != + 1) { + return Result< + std::pair, std::vector>>:: + error("Failed to get authentication tag"); } return Result, std::vector>>( std::make_pair(std::move(ciphertext), std::move(tag))); } catch (const std::exception& e) { - return Result, std::vector>>::error( - std::string("AES-GCM encryption error: ") + e.what()); + return Result, std::vector>>:: + error(std::string("AES-GCM encryption error: ") + e.what()); } } Result Encryption::decryptAesGcm( - const std::vector& ciphertext, - const std::vector& key, - const std::vector& iv, - const std::vector& tag) { - + const std::vector& ciphertext, const std::vector& key, + const std::vector& iv, const std::vector& tag) { try { SslCipherContext ctx; // Initialize decryption - if (EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), nullptr, nullptr, nullptr) != 1) { - return Result::error("Failed to initialize AES-GCM decryption"); + if (EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), nullptr, nullptr, + nullptr) != 1) { + return Result::error( + "Failed to initialize AES-GCM decryption"); } // Set IV length if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, - static_cast(iv.size()), nullptr) != 1) { + static_cast(iv.size()), nullptr) != 1) { return Result::error("Failed to set IV length"); } // Set key and IV - if (EVP_DecryptInit_ex(ctx, nullptr, nullptr, key.data(), iv.data()) != 1) { + if (EVP_DecryptInit_ex(ctx, nullptr, nullptr, key.data(), iv.data()) != + 1) { return Result::error("Failed to set key and IV"); } @@ -654,44 +670,47 @@ Result Encryption::decryptAesGcm( int len = 0; int plaintext_len = 0; - if (EVP_DecryptUpdate(ctx, plaintext.data(), &len, - ciphertext.data(), static_cast(ciphertext.size())) != 1) { + if (EVP_DecryptUpdate(ctx, plaintext.data(), &len, ciphertext.data(), + static_cast(ciphertext.size())) != 1) { return Result::error("Failed to decrypt data"); } plaintext_len = len; // Set authentication tag if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, - static_cast(tag.size()), - const_cast(tag.data())) != 1) { - return Result::error("Failed to set authentication tag"); + static_cast(tag.size()), + const_cast(tag.data())) != 1) { + return Result::error( + "Failed to set authentication tag"); } // Finalize decryption (this verifies the tag) if (EVP_DecryptFinal_ex(ctx, plaintext.data() + len, &len) != 1) { - return Result::error("Authentication verification failed"); + return Result::error( + "Authentication verification failed"); } plaintext_len += len; - return Result(std::string(plaintext.begin(), - plaintext.begin() + plaintext_len)); + return Result( + std::string(plaintext.begin(), plaintext.begin() + plaintext_len)); } catch (const std::exception& e) { - return Result::error(std::string("AES-GCM decryption error: ") + e.what()); + return Result::error( + std::string("AES-GCM decryption error: ") + e.what()); } } Result> Encryption::encryptAesCbc( - std::string_view plaintext, - const std::vector& key, + std::string_view plaintext, const std::vector& key, const std::vector& iv) { - try { SslCipherContext ctx; // Initialize encryption - if (EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), nullptr, key.data(), iv.data()) != 1) { - return Result>::error("Failed to initialize AES-CBC encryption"); + if (EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), nullptr, key.data(), + iv.data()) != 1) { + return Result>::error( + "Failed to initialize AES-CBC encryption"); } // Encrypt @@ -699,16 +718,19 @@ Result> Encryption::encryptAesCbc( int len = 0; int ciphertext_len = 0; - if (EVP_EncryptUpdate(ctx, ciphertext.data(), &len, - reinterpret_cast(plaintext.data()), - static_cast(plaintext.length())) != 1) { - return Result>::error("Failed to encrypt data"); + if (EVP_EncryptUpdate( + ctx, ciphertext.data(), &len, + reinterpret_cast(plaintext.data()), + static_cast(plaintext.length())) != 1) { + return Result>::error( + "Failed to encrypt data"); } ciphertext_len = len; // Finalize encryption (adds padding) if (EVP_EncryptFinal_ex(ctx, ciphertext.data() + len, &len) != 1) { - return Result>::error("Failed to finalize encryption"); + return Result>::error( + "Failed to finalize encryption"); } ciphertext_len += len; ciphertext.resize(ciphertext_len); @@ -716,21 +738,22 @@ Result> Encryption::encryptAesCbc( return Result>(std::move(ciphertext)); } catch (const std::exception& e) { - return Result>::error(std::string("AES-CBC encryption error: ") + e.what()); + return Result>::error( + std::string("AES-CBC encryption error: ") + e.what()); } } Result Encryption::decryptAesCbc( - const std::vector& ciphertext, - const std::vector& key, + const std::vector& ciphertext, const std::vector& key, const std::vector& iv) { - try { SslCipherContext ctx; // Initialize decryption - if (EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), nullptr, key.data(), iv.data()) != 1) { - return Result::error("Failed to initialize AES-CBC decryption"); + if (EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), nullptr, key.data(), + iv.data()) != 1) { + return Result::error( + "Failed to initialize AES-CBC decryption"); } // Decrypt @@ -738,23 +761,25 @@ Result Encryption::decryptAesCbc( int len = 0; int plaintext_len = 0; - if (EVP_DecryptUpdate(ctx, plaintext.data(), &len, - ciphertext.data(), static_cast(ciphertext.size())) != 1) { + if (EVP_DecryptUpdate(ctx, plaintext.data(), &len, ciphertext.data(), + static_cast(ciphertext.size())) != 1) { return Result::error("Failed to decrypt data"); } plaintext_len = len; // Finalize decryption (removes padding) if (EVP_DecryptFinal_ex(ctx, plaintext.data() + len, &len) != 1) { - return Result::error("Decryption failed or invalid padding"); + return Result::error( + "Decryption failed or invalid padding"); } plaintext_len += len; - return Result(std::string(plaintext.begin(), - plaintext.begin() + plaintext_len)); + return Result( + std::string(plaintext.begin(), plaintext.begin() + plaintext_len)); } catch (const std::exception& e) { - return Result::error(std::string("AES-CBC decryption error: ") + e.what()); + return Result::error( + std::string("AES-CBC decryption error: ") + e.what()); } } @@ -775,9 +800,9 @@ size_t Encryption::getKeySize(EncryptionOptions::Method method) { switch (method) { case EncryptionOptions::Method::AES_GCM: case EncryptionOptions::Method::AES_CBC: - return 32; // 256 bits + return 32; // 256 bits case EncryptionOptions::Method::CHACHA20_POLY1305: - return 32; // 256 bits + return 32; // 256 bits default: return 0; } @@ -786,11 +811,11 @@ size_t Encryption::getKeySize(EncryptionOptions::Method method) { size_t Encryption::getIvSize(EncryptionOptions::Method method) { switch (method) { case EncryptionOptions::Method::AES_GCM: - return 12; // 96 bits for GCM + return 12; // 96 bits for GCM case EncryptionOptions::Method::AES_CBC: - return 16; // 128 bits for CBC + return 16; // 128 bits for CBC case EncryptionOptions::Method::CHACHA20_POLY1305: - return 12; // 96 bits + return 12; // 96 bits default: return 0; } diff --git a/atom/secret/encryption.hpp b/atom/secret/encryption.hpp index 0566f95f..985e92cc 100644 --- a/atom/secret/encryption.hpp +++ b/atom/secret/encryption.hpp @@ -3,8 +3,8 @@ #include #include -#include #include +#include #include #include @@ -84,7 +84,7 @@ class SecureMemory { * @tparam T Type of vector elements. * @param vec Vector to clear. */ - template + template static void secureClear(std::vector& vec) noexcept; /** @@ -121,7 +121,7 @@ class SecureMemory { /** * @brief RAII wrapper for secure memory allocation. */ -template +template class SecureBuffer { private: T* data_; @@ -200,10 +200,8 @@ class KeyDerivation { * @return Result containing the derived key or error message. */ static Result> deriveKey( - std::string_view password, - const std::vector& salt, - int iterations, - size_t keyLength); + std::string_view password, const std::vector& salt, + int iterations, size_t keyLength); /** * @brief Generates a cryptographically secure random salt. @@ -224,12 +222,12 @@ class KeyDerivation { * @brief Encrypted data container with metadata. */ struct EncryptedData { - std::vector ciphertext; ///< The encrypted data. - std::vector iv; ///< Initialization vector. - std::vector salt; ///< Salt used for key derivation. - std::vector tag; ///< Authentication tag (for AEAD modes). - EncryptionOptions::Method method; ///< Encryption method used. - int keyIterations; ///< PBKDF2 iterations used. + std::vector ciphertext; ///< The encrypted data. + std::vector iv; ///< Initialization vector. + std::vector salt; ///< Salt used for key derivation. + std::vector tag; ///< Authentication tag (for AEAD modes). + EncryptionOptions::Method method; ///< Encryption method used. + int keyIterations; ///< PBKDF2 iterations used. /** * @brief Serializes the encrypted data to a binary format. @@ -257,10 +255,9 @@ class Encryption { * @param options Encryption options. * @return Result containing encrypted data or error message. */ - static Result encrypt( - std::string_view plaintext, - std::string_view password, - const EncryptionOptions& options = {}); + static Result encrypt(std::string_view plaintext, + std::string_view password, + const EncryptionOptions& options = {}); /** * @brief Decrypts data using the provided password. @@ -268,9 +265,8 @@ class Encryption { * @param password The password for decryption. * @return Result containing decrypted plaintext or error message. */ - static Result decrypt( - const EncryptedData& encryptedData, - std::string_view password); + static Result decrypt(const EncryptedData& encryptedData, + std::string_view password); /** * @brief Encrypts data with a pre-derived key. @@ -280,8 +276,7 @@ class Encryption { * @return Result containing encrypted data or error message. */ static Result encryptWithKey( - std::string_view plaintext, - const std::vector& key, + std::string_view plaintext, const std::vector& key, const EncryptionOptions& options = {}); /** @@ -291,8 +286,7 @@ class Encryption { * @return Result containing decrypted plaintext or error message. */ static Result decryptWithKey( - const EncryptedData& encryptedData, - const std::vector& key); + const EncryptedData& encryptedData, const std::vector& key); private: /** @@ -303,10 +297,8 @@ class Encryption { * @return Result containing encrypted data and tag or error message. */ static Result, std::vector>> - encryptAesGcm( - std::string_view plaintext, - const std::vector& key, - const std::vector& iv); + encryptAesGcm(std::string_view plaintext, const std::vector& key, + const std::vector& iv); /** * @brief Decrypts using AES-GCM. @@ -317,10 +309,8 @@ class Encryption { * @return Result containing decrypted plaintext or error message. */ static Result decryptAesGcm( - const std::vector& ciphertext, - const std::vector& key, - const std::vector& iv, - const std::vector& tag); + const std::vector& ciphertext, const std::vector& key, + const std::vector& iv, const std::vector& tag); /** * @brief Encrypts using AES-CBC. @@ -330,8 +320,7 @@ class Encryption { * @return Result containing encrypted data or error message. */ static Result> encryptAesCbc( - std::string_view plaintext, - const std::vector& key, + std::string_view plaintext, const std::vector& key, const std::vector& iv); /** @@ -342,8 +331,7 @@ class Encryption { * @return Result containing decrypted plaintext or error message. */ static Result decryptAesCbc( - const std::vector& ciphertext, - const std::vector& key, + const std::vector& ciphertext, const std::vector& key, const std::vector& iv); /** diff --git a/atom/secret/integration_test.cpp b/atom/secret/integration_test.cpp index 6a6cf4a3..f5054d17 100644 --- a/atom/secret/integration_test.cpp +++ b/atom/secret/integration_test.cpp @@ -1,8 +1,8 @@ #include "password_manager.hpp" #include "storage.hpp" -#include #include +#include namespace atom::secret { @@ -13,7 +13,7 @@ class IntegrationTest { public: static bool runBasicTests() { std::cout << "Running basic integration tests...\n"; - + // Test 1: Storage creation auto storage = SecureStorage::create("atom-test"); if (!storage) { @@ -21,17 +21,17 @@ class IntegrationTest { return false; } std::cout << "✓ Storage backend created successfully\n"; - + // Test 2: Basic storage operations const std::string testKey = "test_key"; const std::string testData = "test_data_12345"; - + if (!storage->store(testKey, testData)) { std::cerr << "Failed to store test data\n"; return false; } std::cout << "✓ Data stored successfully\n"; - + std::string retrievedData = storage->retrieve(testKey); if (retrievedData != testData) { std::cerr << "Retrieved data doesn't match stored data\n"; @@ -40,7 +40,7 @@ class IntegrationTest { return false; } std::cout << "✓ Data retrieved successfully\n"; - + // Test 3: getAllKeys functionality auto keys = storage->getAllKeys(); bool foundKey = false; @@ -55,14 +55,14 @@ class IntegrationTest { return false; } std::cout << "✓ getAllKeys() working correctly\n"; - + // Test 4: Data removal if (!storage->remove(testKey)) { std::cerr << "Failed to remove test data\n"; return false; } std::cout << "✓ Data removed successfully\n"; - + // Verify removal std::string removedData = storage->retrieve(testKey); if (!removedData.empty()) { @@ -70,25 +70,25 @@ class IntegrationTest { return false; } std::cout << "✓ Data removal verified\n"; - + return true; } - + static bool runPasswordManagerTests() { std::cout << "\nRunning PasswordManager integration tests...\n"; - + // Test 1: PasswordManager initialization PasswordManager manager; PasswordManagerSettings settings; settings.minPasswordLength = 12; settings.autoLockTimeoutSeconds = 300; - + if (!manager.initialize("test_master_password_123", settings)) { std::cerr << "Failed to initialize PasswordManager\n"; return false; } std::cout << "✓ PasswordManager initialized successfully\n"; - + // Test 2: Password storage and retrieval PasswordEntry entry; entry.title = "Test Website"; @@ -99,13 +99,13 @@ class IntegrationTest { entry.category = PasswordCategory::Personal; entry.created = std::chrono::system_clock::now(); entry.modified = entry.created; - + if (!manager.storePassword("test_entry", entry)) { std::cerr << "Failed to store password entry\n"; return false; } std::cout << "✓ Password entry stored successfully\n"; - + PasswordEntry retrievedEntry = manager.retrievePassword("test_entry"); if (retrievedEntry.password != entry.password || retrievedEntry.username != entry.username || @@ -114,7 +114,7 @@ class IntegrationTest { return false; } std::cout << "✓ Password entry retrieved successfully\n"; - + // Test 3: Search functionality auto searchResults = manager.searchPasswords("Test"); if (searchResults.empty()) { @@ -122,15 +122,16 @@ class IntegrationTest { return false; } std::cout << "✓ Search functionality working\n"; - + // Test 4: Password generation - std::string generatedPassword = manager.generatePassword(16, true, true, true); + std::string generatedPassword = + manager.generatePassword(16, true, true, true); if (generatedPassword.empty() || generatedPassword.length() != 16) { std::cerr << "Password generation failed\n"; return false; } std::cout << "✓ Password generation working\n"; - + // Test 5: Lock/unlock functionality manager.lock(); if (!manager.isLocked()) { @@ -138,7 +139,7 @@ class IntegrationTest { return false; } std::cout << "✓ Lock functionality working\n"; - + if (!manager.unlock("test_master_password_123")) { std::cerr << "Failed to unlock manager\n"; return false; @@ -148,34 +149,38 @@ class IntegrationTest { return false; } std::cout << "✓ Unlock functionality working\n"; - + // Test 6: Export/Import functionality auto exportResult = manager.exportToJson(); if (exportResult.isError()) { - std::cerr << "Failed to export data: " << exportResult.error() << "\n"; + std::cerr << "Failed to export data: " << exportResult.error() + << "\n"; return false; } std::cout << "✓ Export functionality working\n"; - + // Clean up manager.removePassword("test_entry"); - + return true; } - + static bool runAllTests() { std::cout << "=== Atom Secret Module Integration Tests ===\n\n"; - + bool basicTestsPass = runBasicTests(); bool managerTestsPass = runPasswordManagerTests(); - + std::cout << "\n=== Test Results ===\n"; - std::cout << "Basic Storage Tests: " << (basicTestsPass ? "PASS" : "FAIL") << "\n"; - std::cout << "PasswordManager Tests: " << (managerTestsPass ? "PASS" : "FAIL") << "\n"; - + std::cout << "Basic Storage Tests: " + << (basicTestsPass ? "PASS" : "FAIL") << "\n"; + std::cout << "PasswordManager Tests: " + << (managerTestsPass ? "PASS" : "FAIL") << "\n"; + bool allTestsPass = basicTestsPass && managerTestsPass; - std::cout << "Overall Result: " << (allTestsPass ? "PASS" : "FAIL") << "\n"; - + std::cout << "Overall Result: " << (allTestsPass ? "PASS" : "FAIL") + << "\n"; + return allTestsPass; } }; diff --git a/atom/secret/password_manager.cpp b/atom/secret/password_manager.cpp index 45a8da48..2b0f346a 100644 --- a/atom/secret/password_manager.cpp +++ b/atom/secret/password_manager.cpp @@ -15,12 +15,9 @@ namespace atom::secret { PasswordManager::PasswordManager() : storage_(SecureStorage::create("atom-password-manager")), isLocked_(true), - lastActivity_(std::chrono::system_clock::now()) { -} + lastActivity_(std::chrono::system_clock::now()) {} -PasswordManager::~PasswordManager() { - lock(); -} +PasswordManager::~PasswordManager() { lock(); } PasswordManager::PasswordManager(PasswordManager&& other) noexcept : storage_(std::move(other.storage_)), @@ -29,22 +26,21 @@ PasswordManager::PasswordManager(PasswordManager&& other) noexcept masterSalt_(std::move(other.masterSalt_)), isLocked_(other.isLocked_), lastActivity_(other.lastActivity_) { - other.isLocked_ = true; other.lastActivity_ = std::chrono::system_clock::now(); } PasswordManager& PasswordManager::operator=(PasswordManager&& other) noexcept { if (this != &other) { - lock(); // Clear current state - + lock(); // Clear current state + storage_ = std::move(other.storage_); settings_ = std::move(other.settings_); masterKey_ = std::move(other.masterKey_); masterSalt_ = std::move(other.masterSalt_); isLocked_ = other.isLocked_; lastActivity_ = other.lastActivity_; - + other.isLocked_ = true; other.lastActivity_ = std::chrono::system_clock::now(); } @@ -52,75 +48,76 @@ PasswordManager& PasswordManager::operator=(PasswordManager&& other) noexcept { } bool PasswordManager::initialize(std::string_view masterPassword, - const PasswordManagerSettings& settings) { + const PasswordManagerSettings& settings) { std::lock_guard lock(mutex_); - + if (masterPassword.empty()) { return false; } - + settings_ = settings; - + // Generate a new salt for the master key masterSalt_.resize(32); - if (RAND_bytes(masterSalt_.data(), static_cast(masterSalt_.size())) != 1) { + if (RAND_bytes(masterSalt_.data(), static_cast(masterSalt_.size())) != + 1) { return false; } - + // Derive the master key auto keyResult = deriveMasterKey(masterPassword); if (keyResult.isError()) { return false; } - + masterKey_ = std::move(keyResult.value()); isLocked_ = false; updateLastActivity(); - + // Store the salt in secure storage for future use std::string saltData(masterSalt_.begin(), masterSalt_.end()); if (!storage_->store("__master_salt__", saltData)) { PasswordManager::lock(); return false; } - + return true; } void PasswordManager::lock() { std::lock_guard lock(mutex_); - + // Securely clear sensitive data SecureMemory::secureClear(masterKey_); SecureMemory::secureClear(masterSalt_); - + isLocked_ = true; } bool PasswordManager::unlock(std::string_view masterPassword) { std::lock_guard lock(mutex_); - + if (masterPassword.empty()) { return false; } - + // Retrieve the master salt std::string saltData = storage_->retrieve("__master_salt__"); if (saltData.empty()) { return false; } masterSalt_.assign(saltData.begin(), saltData.end()); - + // Derive the master key auto keyResult = deriveMasterKey(masterPassword); if (keyResult.isError()) { return false; } - + masterKey_ = std::move(keyResult.value()); isLocked_ = false; updateLastActivity(); - + return true; } @@ -130,57 +127,60 @@ bool PasswordManager::isLocked() const noexcept { } bool PasswordManager::changeMasterPassword(std::string_view currentPassword, - std::string_view newPassword) { + std::string_view newPassword) { std::lock_guard lock(mutex_); - + if (!ensureUnlocked() || currentPassword.empty() || newPassword.empty()) { return false; } - + // Verify current password by deriving key auto currentKeyResult = deriveMasterKey(currentPassword); if (currentKeyResult.isError()) { return false; } - + // Check if current key matches stored key - if (!SecureComparison::constantTimeEquals( - currentKeyResult.value().data(), masterKey_.data(), masterKey_.size())) { + if (!SecureComparison::constantTimeEquals(currentKeyResult.value().data(), + masterKey_.data(), + masterKey_.size())) { return false; } - + // Get all stored entries to re-encrypt with new key auto allKeys = getAllKeys(); std::vector> allEntries; - + for (const auto& key : allKeys) { - if (key == "__master_salt__") continue; + if (key == "__master_salt__") + continue; auto entry = retrievePassword(key); if (!entry.password.empty()) { allEntries.emplace_back(key, std::move(entry)); } } - + // Generate new salt and derive new key - if (RAND_bytes(masterSalt_.data(), static_cast(masterSalt_.size())) != 1) { + if (RAND_bytes(masterSalt_.data(), static_cast(masterSalt_.size())) != + 1) { return false; } - + auto newKeyResult = deriveMasterKey(newPassword); if (newKeyResult.isError()) { return false; } - + // Clear old key and set new key SecureMemory::secureClear(masterKey_); masterKey_ = std::move(newKeyResult.value()); - + // Store new salt std::string saltData(masterSalt_.begin(), masterSalt_.end()); if (!storage_->store("__master_salt__", saltData)) { return false; } - + // Re-encrypt and store all entries with new key for (const auto& [key, entry] : allEntries) { if (!storePassword(key, entry)) { @@ -188,103 +188,109 @@ bool PasswordManager::changeMasterPassword(std::string_view currentPassword, return false; } } - + updateLastActivity(); return true; } -bool PasswordManager::storePassword(std::string_view key, const PasswordEntry& entry) { +bool PasswordManager::storePassword(std::string_view key, + const PasswordEntry& entry) { std::lock_guard lock(mutex_); - + if (!ensureUnlocked() || key.empty()) { return false; } - + // Serialize the entry to JSON auto jsonResult = JsonSerializer::serializeEntry(entry); if (jsonResult.isError()) { return false; } - + // Encrypt the JSON data auto encryptedResult = encryptData(jsonResult.value()); if (encryptedResult.isError()) { return false; } - + // Serialize the encrypted data for storage std::ostringstream oss; const auto& encrypted = encryptedResult.value(); - - // Store as binary data: method(1) + salt_len(4) + salt + iv_len(4) + iv + tag_len(4) + tag + data - oss.write(reinterpret_cast(&encrypted.method), sizeof(encrypted.method)); - + + // Store as binary data: method(1) + salt_len(4) + salt + iv_len(4) + iv + + // tag_len(4) + tag + data + oss.write(reinterpret_cast(&encrypted.method), + sizeof(encrypted.method)); + uint32_t saltLen = static_cast(encrypted.salt.size()); oss.write(reinterpret_cast(&saltLen), sizeof(saltLen)); oss.write(reinterpret_cast(encrypted.salt.data()), saltLen); - + uint32_t ivLen = static_cast(encrypted.iv.size()); oss.write(reinterpret_cast(&ivLen), sizeof(ivLen)); oss.write(reinterpret_cast(encrypted.iv.data()), ivLen); - + uint32_t tagLen = static_cast(encrypted.tag.size()); oss.write(reinterpret_cast(&tagLen), sizeof(tagLen)); oss.write(reinterpret_cast(encrypted.tag.data()), tagLen); - - oss.write(reinterpret_cast(encrypted.ciphertext.data()), encrypted.ciphertext.size()); - + + oss.write(reinterpret_cast(encrypted.ciphertext.data()), + encrypted.ciphertext.size()); + updateLastActivity(); return storage_->store(std::string(key), oss.str()); } PasswordEntry PasswordManager::retrievePassword(std::string_view key) { std::lock_guard lock(mutex_); - + if (!ensureUnlocked() || key.empty()) { return PasswordEntry{}; } - + // Retrieve encrypted data from storage std::string data = storage_->retrieve(std::string(key)); if (data.empty()) { return PasswordEntry{}; } std::istringstream iss(data); - + // Deserialize encrypted data EncryptedData encrypted; - - iss.read(reinterpret_cast(&encrypted.method), sizeof(encrypted.method)); - + + iss.read(reinterpret_cast(&encrypted.method), + sizeof(encrypted.method)); + uint32_t saltLen, ivLen, tagLen; iss.read(reinterpret_cast(&saltLen), sizeof(saltLen)); encrypted.salt.resize(saltLen); iss.read(reinterpret_cast(encrypted.salt.data()), saltLen); - + iss.read(reinterpret_cast(&ivLen), sizeof(ivLen)); encrypted.iv.resize(ivLen); iss.read(reinterpret_cast(encrypted.iv.data()), ivLen); - + iss.read(reinterpret_cast(&tagLen), sizeof(tagLen)); encrypted.tag.resize(tagLen); iss.read(reinterpret_cast(encrypted.tag.data()), tagLen); - + size_t dataLen = data.size() - iss.tellg(); encrypted.ciphertext.resize(dataLen); iss.read(reinterpret_cast(encrypted.ciphertext.data()), dataLen); - + // Decrypt the data auto decryptedResult = decryptData(encrypted); if (decryptedResult.isError()) { return PasswordEntry{}; } - + // Deserialize the JSON to PasswordEntry - auto entryResult = JsonSerializer::deserializeEntry(decryptedResult.value()); + auto entryResult = + JsonSerializer::deserializeEntry(decryptedResult.value()); if (entryResult.isError()) { return PasswordEntry{}; } - + updateLastActivity(); return entryResult.value(); } @@ -310,25 +316,21 @@ std::vector PasswordManager::getAllKeys() { auto allKeys = storage_->getAllKeys(); // Filter out internal keys - allKeys.erase( - std::remove_if(allKeys.begin(), allKeys.end(), - [](const std::string& key) { - return key.starts_with("__") && key.ends_with("__"); - }), - allKeys.end()); + allKeys.erase(std::remove_if(allKeys.begin(), allKeys.end(), + [](const std::string& key) { + return key.starts_with("__") && + key.ends_with("__"); + }), + allKeys.end()); updateLastActivity(); return allKeys; } -std::vector> PasswordManager::searchPasswords( - std::string_view query, - bool searchInTitle, - bool searchInUsername, - bool searchInUrl, - bool searchInNotes, - bool searchInTags) { - +std::vector> +PasswordManager::searchPasswords(std::string_view query, bool searchInTitle, + bool searchInUsername, bool searchInUrl, + bool searchInNotes, bool searchInTags) { std::lock_guard lock(mutex_); if (!ensureUnlocked() || query.empty()) { @@ -346,7 +348,8 @@ std::vector> PasswordManager::searchPasswo for (const auto& key : allKeys) { auto entry = retrievePassword(key); - if (entry.password.empty()) continue; + if (entry.password.empty()) + continue; bool matches = false; @@ -360,10 +363,14 @@ std::vector> PasswordManager::searchPasswo return lowerText.find(lowerQuery) != std::string::npos; }; - if (searchInTitle && contains(entry.title)) matches = true; - if (searchInUsername && contains(entry.username)) matches = true; - if (searchInUrl && contains(entry.url)) matches = true; - if (searchInNotes && contains(entry.notes)) matches = true; + if (searchInTitle && contains(entry.title)) + matches = true; + if (searchInUsername && contains(entry.username)) + matches = true; + if (searchInUrl && contains(entry.url)) + matches = true; + if (searchInNotes && contains(entry.notes)) + matches = true; if (searchInTags) { for (const auto& tag : entry.tags) { @@ -383,9 +390,8 @@ std::vector> PasswordManager::searchPasswo return results; } -std::vector> PasswordManager::filterByCategory( - PasswordCategory category) { - +std::vector> +PasswordManager::filterByCategory(PasswordCategory category) { std::lock_guard lock(mutex_); if (!ensureUnlocked()) { @@ -397,7 +403,8 @@ std::vector> PasswordManager::filterByCate for (const auto& key : allKeys) { auto entry = retrievePassword(key); - if (entry.password.empty()) continue; + if (entry.password.empty()) + continue; if (entry.category == category) { results.emplace_back(key, std::move(entry)); @@ -408,9 +415,8 @@ std::vector> PasswordManager::filterByCate return results; } -std::vector> PasswordManager::getExpiringPasswords( - int daysAhead) { - +std::vector> +PasswordManager::getExpiringPasswords(int daysAhead) { std::lock_guard lock(mutex_); if (!ensureUnlocked()) { @@ -424,7 +430,8 @@ std::vector> PasswordManager::getExpiringP for (const auto& key : allKeys) { auto entry = retrievePassword(key); - if (entry.password.empty()) continue; + if (entry.password.empty()) + continue; if (entry.expires != std::chrono::system_clock::time_point{} && entry.expires <= futureTime) { @@ -436,10 +443,9 @@ std::vector> PasswordManager::getExpiringP return results; } -std::string PasswordManager::generatePassword(int length, - bool includeUppercase, - bool includeNumbers, - bool includeSpecial) { +std::string PasswordManager::generatePassword(int length, bool includeUppercase, + bool includeNumbers, + bool includeSpecial) { std::lock_guard lock(mutex_); if (!ensureUnlocked()) { @@ -458,7 +464,8 @@ std::string PasswordManager::generatePassword(int length, return result.isError() ? "" : result.value(); } -PasswordValidator::AnalysisResult PasswordManager::analyzePassword(std::string_view password) { +PasswordValidator::AnalysisResult PasswordManager::analyzePassword( + std::string_view password) { std::lock_guard lock(mutex_); updateLastActivity(); return PasswordValidator::analyzePassword(password); @@ -485,7 +492,8 @@ Result PasswordManager::exportToJson() { return JsonSerializer::serializeEntries(entries); } -Result PasswordManager::importFromJson(const std::string& json, bool overwriteExisting) { +Result PasswordManager::importFromJson(const std::string& json, + bool overwriteExisting) { std::lock_guard lock(mutex_); if (!ensureUnlocked()) { @@ -494,7 +502,8 @@ Result PasswordManager::importFromJson(const std::string& json, bool overwr auto entriesResult = JsonSerializer::deserializeEntries(json); if (entriesResult.isError()) { - return Result::error("Failed to parse JSON: " + entriesResult.error()); + return Result::error("Failed to parse JSON: " + + entriesResult.error()); } const auto& entries = entriesResult.value(); @@ -511,7 +520,7 @@ Result PasswordManager::importFromJson(const std::string& json, bool overwr if (!overwriteExisting) { auto existingEntry = retrievePassword(key); if (!existingEntry.password.empty()) { - continue; // Skip existing entry + continue; // Skip existing entry } } @@ -551,7 +560,8 @@ PasswordManager::Statistics PasswordManager::getStatistics() { for (const auto& key : allKeys) { auto entry = retrievePassword(key); - if (entry.password.empty()) continue; + if (entry.password.empty()) + continue; stats.totalEntries++; @@ -569,7 +579,8 @@ PasswordManager::Statistics PasswordManager::getStatistics() { } // Check for duplicates - if (std::find(passwords.begin(), passwords.end(), entry.password) != passwords.end()) { + if (std::find(passwords.begin(), passwords.end(), entry.password) != + passwords.end()) { stats.duplicatePasswords++; } else { passwords.push_back(entry.password); @@ -589,30 +600,32 @@ PasswordManager::Statistics PasswordManager::getStatistics() { // Private Methods // ============================================================================ -Result> PasswordManager::deriveMasterKey(std::string_view masterPassword) { - return KeyDerivation::deriveKey( - masterPassword, - masterSalt_, - settings_.encryptionOptions.keyIterations, - 32 // 256-bit key +Result> PasswordManager::deriveMasterKey( + std::string_view masterPassword) { + return KeyDerivation::deriveKey(masterPassword, masterSalt_, + settings_.encryptionOptions.keyIterations, + 32 // 256-bit key ); } Result PasswordManager::encryptData(const std::string& data) { - return Encryption::encryptWithKey(data, masterKey_, settings_.encryptionOptions); + return Encryption::encryptWithKey(data, masterKey_, + settings_.encryptionOptions); } -Result PasswordManager::decryptData(const EncryptedData& encryptedData) { +Result PasswordManager::decryptData( + const EncryptedData& encryptedData) { return Encryption::decryptWithKey(encryptedData, masterKey_); } void PasswordManager::checkAutoLock() { if (settings_.autoLockTimeoutSeconds <= 0) { - return; // Auto-lock disabled + return; // Auto-lock disabled } auto now = std::chrono::system_clock::now(); - auto elapsed = std::chrono::duration_cast(now - lastActivity_); + auto elapsed = + std::chrono::duration_cast(now - lastActivity_); if (elapsed.count() >= settings_.autoLockTimeoutSeconds) { PasswordManager::lock(); diff --git a/atom/secret/password_manager.hpp b/atom/secret/password_manager.hpp index 456fb3d6..003c2768 100644 --- a/atom/secret/password_manager.hpp +++ b/atom/secret/password_manager.hpp @@ -42,7 +42,8 @@ namespace atom::secret { /** - * @brief Main password manager class providing secure password storage and management. + * @brief Main password manager class providing secure password storage and + * management. */ class PasswordManager { public: @@ -71,7 +72,7 @@ class PasswordManager { * @return True if initialization succeeded, false otherwise. */ bool initialize(std::string_view masterPassword, - const PasswordManagerSettings& settings = {}); + const PasswordManagerSettings& settings = {}); /** * @brief Locks the password manager, clearing sensitive data from memory. @@ -98,7 +99,7 @@ class PasswordManager { * @return True if change succeeded, false otherwise. */ bool changeMasterPassword(std::string_view currentPassword, - std::string_view newPassword); + std::string_view newPassword); /** * @brief Stores a password entry. @@ -139,12 +140,9 @@ class PasswordManager { * @return Vector of matching password entries with their keys. */ std::vector> searchPasswords( - std::string_view query, - bool searchInTitle = true, - bool searchInUsername = true, - bool searchInUrl = true, - bool searchInNotes = false, - bool searchInTags = true); + std::string_view query, bool searchInTitle = true, + bool searchInUsername = true, bool searchInUrl = true, + bool searchInNotes = false, bool searchInTags = true); /** * @brief Filters password entries by category. @@ -170,17 +168,17 @@ class PasswordManager { * @param includeSpecial Include special characters. * @return Generated password or empty string on failure. */ - std::string generatePassword(int length = 0, - bool includeUppercase = true, - bool includeNumbers = true, - bool includeSpecial = true); + std::string generatePassword(int length = 0, bool includeUppercase = true, + bool includeNumbers = true, + bool includeSpecial = true); /** * @brief Analyzes password strength. * @param password Password to analyze. * @return Password analysis results. */ - PasswordValidator::AnalysisResult analyzePassword(std::string_view password); + PasswordValidator::AnalysisResult analyzePassword( + std::string_view password); /** * @brief Exports all password entries to JSON. @@ -194,7 +192,8 @@ class PasswordManager { * @param overwriteExisting Whether to overwrite existing entries. * @return Result containing number of imported entries or error message. */ - Result importFromJson(const std::string& json, bool overwriteExisting = false); + Result importFromJson(const std::string& json, + bool overwriteExisting = false); /** * @brief Gets the current password manager settings. @@ -228,7 +227,8 @@ class PasswordManager { * @param masterPassword Master password. * @return Result containing derived key or error message. */ - Result> deriveMasterKey(std::string_view masterPassword); + Result> deriveMasterKey( + std::string_view masterPassword); /** * @brief Encrypts data using the master key. diff --git a/atom/secret/password_utils.cpp b/atom/secret/password_utils.cpp index 7cf45c72..afb3fd04 100644 --- a/atom/secret/password_utils.cpp +++ b/atom/secret/password_utils.cpp @@ -21,7 +21,8 @@ Result PasswordGenerator::generatePassword() { return generatePassword(GenerationOptions{}); } -Result PasswordGenerator::generatePassword(const GenerationOptions& options) { +Result PasswordGenerator::generatePassword( + const GenerationOptions& options) { // Validate options std::string validationError = validateOptions(options); if (!validationError.empty()) { @@ -31,7 +32,8 @@ Result PasswordGenerator::generatePassword(const GenerationOptions& // Build character set std::string charset = buildCharacterSet(options); if (charset.empty()) { - return Result("No characters available for password generation"); + return Result( + "No characters available for password generation"); } // Generate random password @@ -56,41 +58,36 @@ Result PasswordGenerator::generatePassword(const GenerationOptions& } Result PasswordGenerator::generatePassword( - const PasswordManagerSettings& settings, - int length) { - + const PasswordManagerSettings& settings, int length) { GenerationOptions options; options.length = (length > 0) ? length : settings.minPasswordLength; options.includeLowercase = true; options.includeUppercase = settings.requireMixedCase; options.includeDigits = settings.requireNumbers; options.includeSpecial = settings.requireSpecialChars; - options.excludeAmbiguous = true; // Default to excluding ambiguous chars + options.excludeAmbiguous = true; // Default to excluding ambiguous chars return generatePassword(options); } Result PasswordGenerator::generateMemorablePassword( - int wordCount, - const std::string& separator, - bool includeNumbers) { - + int wordCount, const std::string& separator, bool includeNumbers) { if (wordCount < 2 || wordCount > 10) { return Result("Word count must be between 2 and 10"); } // Simple word list for memorable passwords static const std::vector words = { - "apple", "brave", "chair", "dance", "eagle", "flame", "grace", "house", - "image", "juice", "knife", "light", "music", "night", "ocean", "peace", - "quiet", "river", "stone", "table", "unity", "voice", "water", "youth", - "zebra", "beach", "cloud", "dream", "earth", "field", "green", "happy", - "island", "magic", "north", "power", "quick", "smile", "trust", "world" - }; + "apple", "brave", "chair", "dance", "eagle", "flame", "grace", + "house", "image", "juice", "knife", "light", "music", "night", + "ocean", "peace", "quiet", "river", "stone", "table", "unity", + "voice", "water", "youth", "zebra", "beach", "cloud", "dream", + "earth", "field", "green", "happy", "island", "magic", "north", + "power", "quick", "smile", "trust", "world"}; std::string password; std::vector randomBytes(wordCount * 2); - + if (RAND_bytes(randomBytes.data(), wordCount * 2) != 1) { return Result("Failed to generate secure random numbers"); } @@ -99,10 +96,10 @@ Result PasswordGenerator::generateMemorablePassword( if (i > 0) { password += separator; } - + size_t wordIndex = randomBytes[i] % words.size(); password += words[wordIndex]; - + if (includeNumbers && i < wordCount - 1) { int number = randomBytes[wordCount + i] % 100; password += std::to_string(number); @@ -132,7 +129,8 @@ Result PasswordGenerator::generatePin(int length) { return Result(std::move(pin)); } -std::string PasswordGenerator::buildCharacterSet(const GenerationOptions& options) { +std::string PasswordGenerator::buildCharacterSet( + const GenerationOptions& options) { std::string charset; if (options.includeLowercase) { @@ -155,7 +153,8 @@ std::string PasswordGenerator::buildCharacterSet(const GenerationOptions& option if (options.excludeAmbiguous) { std::string ambiguous = CharacterSets::AMBIGUOUS; for (char c : ambiguous) { - charset.erase(std::remove(charset.begin(), charset.end(), c), charset.end()); + charset.erase(std::remove(charset.begin(), charset.end(), c), + charset.end()); } } @@ -166,19 +165,20 @@ std::string PasswordGenerator::buildCharacterSet(const GenerationOptions& option return charset; } -std::string PasswordGenerator::validateOptions(const GenerationOptions& options) { +std::string PasswordGenerator::validateOptions( + const GenerationOptions& options) { if (options.length < 1 || options.length > 1000) { return "Password length must be between 1 and 1000"; } - if (!options.includeLowercase && !options.includeUppercase && - !options.includeDigits && !options.includeSpecial && + if (!options.includeLowercase && !options.includeUppercase && + !options.includeDigits && !options.includeSpecial && options.customCharacters.empty()) { return "At least one character type must be enabled"; } - int minRequired = options.minLowercase + options.minUppercase + - options.minDigits + options.minSpecial; + int minRequired = options.minLowercase + options.minUppercase + + options.minDigits + options.minSpecial; if (minRequired > options.length) { return "Minimum character requirements exceed password length"; } @@ -187,25 +187,27 @@ std::string PasswordGenerator::validateOptions(const GenerationOptions& options) } std::string PasswordGenerator::ensureMinimumRequirements( - std::string password, - const GenerationOptions& options) { - + std::string password, const GenerationOptions& options) { // Count existing character types int lowercase = 0, uppercase = 0, digits = 0, special = 0; - + for (char c : password) { - if (std::islower(c)) lowercase++; - else if (std::isupper(c)) uppercase++; - else if (std::isdigit(c)) digits++; - else special++; + if (std::islower(c)) + lowercase++; + else if (std::isupper(c)) + uppercase++; + else if (std::isdigit(c)) + digits++; + else + special++; } // Replace characters to meet minimum requirements std::vector randomBytes(password.length()); RAND_bytes(randomBytes.data(), password.length()); - + size_t pos = 0; - + // Ensure minimum lowercase while (lowercase < options.minLowercase && pos < password.length()) { if (!std::islower(password[pos])) { @@ -214,7 +216,7 @@ std::string PasswordGenerator::ensureMinimumRequirements( } pos++; } - + // Ensure minimum uppercase while (uppercase < options.minUppercase && pos < password.length()) { if (!std::isupper(password[pos])) { @@ -250,7 +252,8 @@ std::string PasswordGenerator::ensureMinimumRequirements( // PasswordValidator Implementation // ============================================================================ -PasswordValidator::AnalysisResult PasswordValidator::analyzePassword(std::string_view password) { +PasswordValidator::AnalysisResult PasswordValidator::analyzePassword( + std::string_view password) { AnalysisResult result = {}; if (password.empty()) { @@ -262,10 +265,14 @@ PasswordValidator::AnalysisResult PasswordValidator::analyzePassword(std::string // Check character types for (char c : password) { - if (std::islower(c)) result.hasLowercase = true; - else if (std::isupper(c)) result.hasUppercase = true; - else if (std::isdigit(c)) result.hasDigits = true; - else result.hasSpecial = true; + if (std::islower(c)) + result.hasLowercase = true; + else if (std::isupper(c)) + result.hasUppercase = true; + else if (std::isdigit(c)) + result.hasDigits = true; + else + result.hasSpecial = true; } // Check for patterns @@ -280,32 +287,48 @@ PasswordValidator::AnalysisResult PasswordValidator::analyzePassword(std::string int score = 0; // Length scoring - if (password.length() >= 8) score += 25; - if (password.length() >= 12) score += 25; - if (password.length() >= 16) score += 25; + if (password.length() >= 8) + score += 25; + if (password.length() >= 12) + score += 25; + if (password.length() >= 16) + score += 25; // Character type scoring - if (result.hasLowercase) score += 5; - if (result.hasUppercase) score += 5; - if (result.hasDigits) score += 5; - if (result.hasSpecial) score += 10; + if (result.hasLowercase) + score += 5; + if (result.hasUppercase) + score += 5; + if (result.hasDigits) + score += 5; + if (result.hasSpecial) + score += 10; // Pattern penalties - if (result.hasRepeatedChars) score -= 20; - if (result.hasSequentialChars) score -= 15; - if (result.isCommonPassword) score -= 30; + if (result.hasRepeatedChars) + score -= 20; + if (result.hasSequentialChars) + score -= 15; + if (result.isCommonPassword) + score -= 30; // Entropy bonus - if (result.entropy > 50) score += 15; + if (result.entropy > 50) + score += 15; result.score = std::max(0, std::min(100, score)); // Determine strength - if (result.score < 20) result.strength = PasswordStrength::VeryWeak; - else if (result.score < 40) result.strength = PasswordStrength::Weak; - else if (result.score < 60) result.strength = PasswordStrength::Medium; - else if (result.score < 80) result.strength = PasswordStrength::Strong; - else result.strength = PasswordStrength::VeryStrong; + if (result.score < 20) + result.strength = PasswordStrength::VeryWeak; + else if (result.score < 40) + result.strength = PasswordStrength::Weak; + else if (result.score < 60) + result.strength = PasswordStrength::Medium; + else if (result.score < 80) + result.strength = PasswordStrength::Strong; + else + result.strength = PasswordStrength::VeryStrong; // Generate suggestions if (password.length() < 12) { @@ -337,25 +360,30 @@ PasswordValidator::AnalysisResult PasswordValidator::analyzePassword(std::string } Result PasswordValidator::validatePassword( - std::string_view password, - const PasswordManagerSettings& settings) { - + std::string_view password, const PasswordManagerSettings& settings) { if (password.length() < static_cast(settings.minPasswordLength)) { return Result::error("Password is too short (minimum " + - std::to_string(settings.minPasswordLength) + " characters)"); + std::to_string(settings.minPasswordLength) + + " characters)"); } - bool hasLower = false, hasUpper = false, hasDigit = false, hasSpecial = false; + bool hasLower = false, hasUpper = false, hasDigit = false, + hasSpecial = false; for (char c : password) { - if (std::islower(c)) hasLower = true; - else if (std::isupper(c)) hasUpper = true; - else if (std::isdigit(c)) hasDigit = true; - else hasSpecial = true; + if (std::islower(c)) + hasLower = true; + else if (std::isupper(c)) + hasUpper = true; + else if (std::isdigit(c)) + hasDigit = true; + else + hasSpecial = true; } if (settings.requireMixedCase && (!hasLower || !hasUpper)) { - return Result("Password must contain both uppercase and lowercase letters"); + return Result( + "Password must contain both uppercase and lowercase letters"); } if (settings.requireNumbers && !hasDigit) { @@ -363,7 +391,8 @@ Result PasswordValidator::validatePassword( } if (settings.requireSpecialChars && !hasSpecial) { - return Result("Password must contain at least one special character"); + return Result( + "Password must contain at least one special character"); } return Result(true); @@ -387,18 +416,20 @@ bool PasswordValidator::isCommonPassword(std::string_view password) { lowerPassword += std::tolower(c); } - return std::find(commonPasswords.begin(), commonPasswords.end(), lowerPassword) - != commonPasswords.end(); + return std::find(commonPasswords.begin(), commonPasswords.end(), + lowerPassword) != commonPasswords.end(); } -double PasswordValidator::estimateCrackTime(std::string_view password, double guessesPerSecond) { +double PasswordValidator::estimateCrackTime(std::string_view password, + double guessesPerSecond) { double entropy = calculateEntropy(password); double possibleCombinations = std::pow(2.0, entropy); - return possibleCombinations / (2.0 * guessesPerSecond); // Average case + return possibleCombinations / (2.0 * guessesPerSecond); // Average case } bool PasswordValidator::hasRepeatedPatterns(std::string_view password) { - if (password.length() < 3) return false; + if (password.length() < 3) + return false; // Check for repeated characters (3 or more in a row) for (size_t i = 0; i < password.length() - 2; ++i) { @@ -422,7 +453,8 @@ bool PasswordValidator::hasRepeatedPatterns(std::string_view password) { } bool PasswordValidator::hasSequentialPatterns(std::string_view password) { - if (password.length() < 3) return false; + if (password.length() < 3) + return false; // Check for sequential characters (ascending or descending) for (size_t i = 0; i < password.length() - 2; ++i) { @@ -443,31 +475,40 @@ bool PasswordValidator::hasSequentialPatterns(std::string_view password) { } int PasswordValidator::calculateCharacterSetSize(std::string_view password) { - bool hasLower = false, hasUpper = false, hasDigit = false, hasSpecial = false; + bool hasLower = false, hasUpper = false, hasDigit = false, + hasSpecial = false; for (char c : password) { - if (std::islower(c)) hasLower = true; - else if (std::isupper(c)) hasUpper = true; - else if (std::isdigit(c)) hasDigit = true; - else hasSpecial = true; + if (std::islower(c)) + hasLower = true; + else if (std::isupper(c)) + hasUpper = true; + else if (std::isdigit(c)) + hasDigit = true; + else + hasSpecial = true; } int size = 0; - if (hasLower) size += 26; - if (hasUpper) size += 26; - if (hasDigit) size += 10; - if (hasSpecial) size += 32; // Approximate number of common special characters + if (hasLower) + size += 26; + if (hasUpper) + size += 26; + if (hasDigit) + size += 10; + if (hasSpecial) + size += 32; // Approximate number of common special characters return size; } const std::vector& PasswordValidator::getCommonPasswords() { static const std::vector commonPasswords = { - "password", "123456", "password123", "admin", "qwerty", "letmein", - "welcome", "monkey", "1234567890", "abc123", "111111", "dragon", - "master", "princess", "login", "guest", "solo", "sunshine", "shadow", - "football", "jesus", "michael", "ninja", "mustang", "password1" - }; + "password", "123456", "password123", "admin", "qwerty", + "letmein", "welcome", "monkey", "1234567890", "abc123", + "111111", "dragon", "master", "princess", "login", + "guest", "solo", "sunshine", "shadow", "football", + "jesus", "michael", "ninja", "mustang", "password1"}; return commonPasswords; } @@ -475,7 +516,8 @@ const std::vector& PasswordValidator::getCommonPasswords() { // SecureComparison Implementation // ============================================================================ -bool SecureComparison::constantTimeEquals(std::string_view a, std::string_view b) noexcept { +bool SecureComparison::constantTimeEquals(std::string_view a, + std::string_view b) noexcept { if (a.length() != b.length()) { return false; } @@ -483,7 +525,8 @@ bool SecureComparison::constantTimeEquals(std::string_view a, std::string_view b return constantTimeEquals(a.data(), b.data(), a.length()); } -bool SecureComparison::constantTimeEquals(const void* a, const void* b, size_t size) noexcept { +bool SecureComparison::constantTimeEquals(const void* a, const void* b, + size_t size) noexcept { const unsigned char* pa = static_cast(a); const unsigned char* pb = static_cast(b); diff --git a/atom/secret/password_utils.hpp b/atom/secret/password_utils.hpp index e0a0f020..174fb291 100644 --- a/atom/secret/password_utils.hpp +++ b/atom/secret/password_utils.hpp @@ -30,17 +30,17 @@ class PasswordGenerator { * @brief Options for password generation. */ struct GenerationOptions { - int length = 16; ///< Password length. - bool includeLowercase = true; ///< Include lowercase letters. - bool includeUppercase = true; ///< Include uppercase letters. - bool includeDigits = true; ///< Include digits. - bool includeSpecial = true; ///< Include special characters. - bool excludeAmbiguous = false; ///< Exclude ambiguous characters. - std::string customCharacters; ///< Custom character set. - int minLowercase = 1; ///< Minimum lowercase letters. - int minUppercase = 1; ///< Minimum uppercase letters. - int minDigits = 1; ///< Minimum digits. - int minSpecial = 1; ///< Minimum special characters. + int length = 16; ///< Password length. + bool includeLowercase = true; ///< Include lowercase letters. + bool includeUppercase = true; ///< Include uppercase letters. + bool includeDigits = true; ///< Include digits. + bool includeSpecial = true; ///< Include special characters. + bool excludeAmbiguous = false; ///< Exclude ambiguous characters. + std::string customCharacters; ///< Custom character set. + int minLowercase = 1; ///< Minimum lowercase letters. + int minUppercase = 1; ///< Minimum uppercase letters. + int minDigits = 1; ///< Minimum digits. + int minSpecial = 1; ///< Minimum special characters. }; /** @@ -54,7 +54,8 @@ class PasswordGenerator { * @param options Password generation options. * @return Result containing the generated password or error message. */ - static Result generatePassword(const GenerationOptions& options); + static Result generatePassword( + const GenerationOptions& options); /** * @brief Generates a password based on PasswordManagerSettings. @@ -63,8 +64,7 @@ class PasswordGenerator { * @return Result containing the generated password or error message. */ static Result generatePassword( - const PasswordManagerSettings& settings, - int length = 0); + const PasswordManagerSettings& settings, int length = 0); /** * @brief Generates a memorable password using word lists. @@ -74,8 +74,7 @@ class PasswordGenerator { * @return Result containing the generated password or error message. */ static Result generateMemorablePassword( - int wordCount = 4, - const std::string& separator = "-", + int wordCount = 4, const std::string& separator = "-", bool includeNumbers = true); /** @@ -107,8 +106,7 @@ class PasswordGenerator { * @return Modified password. */ static std::string ensureMinimumRequirements( - std::string password, - const GenerationOptions& options); + std::string password, const GenerationOptions& options); }; /** @@ -120,17 +118,17 @@ class PasswordValidator { * @brief Detailed password analysis results. */ struct AnalysisResult { - PasswordStrength strength; ///< Overall password strength. - int score; ///< Numerical score (0-100). - bool hasLowercase; ///< Contains lowercase letters. - bool hasUppercase; ///< Contains uppercase letters. - bool hasDigits; ///< Contains digits. - bool hasSpecial; ///< Contains special characters. - bool hasRepeatedChars; ///< Contains repeated characters. - bool hasSequentialChars; ///< Contains sequential characters. - bool isCommonPassword; ///< Is a commonly used password. - std::vector suggestions; ///< Improvement suggestions. - double entropy; ///< Password entropy in bits. + PasswordStrength strength; ///< Overall password strength. + int score; ///< Numerical score (0-100). + bool hasLowercase; ///< Contains lowercase letters. + bool hasUppercase; ///< Contains uppercase letters. + bool hasDigits; ///< Contains digits. + bool hasSpecial; ///< Contains special characters. + bool hasRepeatedChars; ///< Contains repeated characters. + bool hasSequentialChars; ///< Contains sequential characters. + bool isCommonPassword; ///< Is a commonly used password. + std::vector suggestions; ///< Improvement suggestions. + double entropy; ///< Password entropy in bits. }; /** @@ -147,8 +145,7 @@ class PasswordValidator { * @return Result containing true if valid or error message. */ static Result validatePassword( - std::string_view password, - const PasswordManagerSettings& settings); + std::string_view password, const PasswordManagerSettings& settings); /** * @brief Calculates password entropy. @@ -170,9 +167,8 @@ class PasswordValidator { * @param guessesPerSecond Guesses per second (default: 1 billion). * @return Estimated crack time in seconds. */ - static double estimateCrackTime( - std::string_view password, - double guessesPerSecond = 1e9); + static double estimateCrackTime(std::string_view password, + double guessesPerSecond = 1e9); private: /** @@ -214,7 +210,8 @@ class SecureComparison { * @param b Second string. * @return True if strings are equal. */ - static bool constantTimeEquals(std::string_view a, std::string_view b) noexcept; + static bool constantTimeEquals(std::string_view a, + std::string_view b) noexcept; /** * @brief Performs constant-time memory comparison. @@ -223,7 +220,8 @@ class SecureComparison { * @param size Size of memory blocks. * @return True if memory blocks are equal. */ - static bool constantTimeEquals(const void* a, const void* b, size_t size) noexcept; + static bool constantTimeEquals(const void* a, const void* b, + size_t size) noexcept; }; } // namespace atom::secret diff --git a/atom/secret/result.hpp b/atom/secret/result.hpp index 81e80a54..3ac1216a 100644 --- a/atom/secret/result.hpp +++ b/atom/secret/result.hpp @@ -3,8 +3,8 @@ #include #include -#include #include +#include namespace atom::secret { @@ -15,7 +15,7 @@ struct Error { explicit Error(std::string&& msg) : message(std::move(msg)) {} }; -} +} // namespace atom::secret namespace atom::secret { @@ -60,18 +60,14 @@ class Result { * @param value The success value. * @return A Result containing the success value. */ - static Result success(const T& value) { - return Result(value); - } + static Result success(const T& value) { return Result(value); } /** * @brief Creates a successful Result (move version). * @param value The success value. * @return A Result containing the success value. */ - static Result success(T&& value) { - return Result(std::move(value)); - } + static Result success(T&& value) { return Result(std::move(value)); } /** * @brief Creates an error Result. diff --git a/atom/secret/serialization.cpp b/atom/secret/serialization.cpp index cef64fb4..14a84a01 100644 --- a/atom/secret/serialization.cpp +++ b/atom/secret/serialization.cpp @@ -1,8 +1,8 @@ #include "serialization.hpp" +#include #include #include -#include namespace atom::secret { @@ -12,7 +12,7 @@ namespace atom::secret { Result JsonSerializer::serializeEntry(const PasswordEntry& entry) { std::ostringstream json; - + json << "{\n"; json << " \"password\": \"" << escapeJsonString(entry.password) << "\",\n"; json << " \"username\": \"" << escapeJsonString(entry.username) << "\",\n"; @@ -22,59 +22,65 @@ Result JsonSerializer::serializeEntry(const PasswordEntry& entry) { json << " \"category\": \"" << categoryToString(entry.category) << "\",\n"; json << " \"tags\": " << serializeStringArray(entry.tags) << ",\n"; json << " \"created\": \"" << timePointToString(entry.created) << "\",\n"; - json << " \"modified\": \"" << timePointToString(entry.modified) << "\",\n"; + json << " \"modified\": \"" << timePointToString(entry.modified) + << "\",\n"; json << " \"expires\": \"" << timePointToString(entry.expires) << "\",\n"; - json << " \"previousPasswords\": " << serializeStringArray(entry.previousPasswords) << "\n"; + json << " \"previousPasswords\": " + << serializeStringArray(entry.previousPasswords) << "\n"; json << "}"; - + return Result(json.str()); } -Result JsonSerializer::deserializeEntry(const std::string& json) { +Result JsonSerializer::deserializeEntry( + const std::string& json) { if (!SimpleJsonParser::isValidJson(json)) { return Result::error("Invalid JSON format"); } - + PasswordEntry entry; - + entry.password = SimpleJsonParser::extractString(json, "password"); entry.username = SimpleJsonParser::extractString(json, "username"); entry.url = SimpleJsonParser::extractString(json, "url"); entry.notes = SimpleJsonParser::extractString(json, "notes"); entry.title = SimpleJsonParser::extractString(json, "title"); - + std::string categoryStr = SimpleJsonParser::extractString(json, "category"); entry.category = stringToCategory(categoryStr); - + std::string tagsArray = SimpleJsonParser::extractArray(json, "tags"); entry.tags = deserializeStringArray(tagsArray); - + std::string createdStr = SimpleJsonParser::extractString(json, "created"); entry.created = stringToTimePoint(createdStr); - + std::string modifiedStr = SimpleJsonParser::extractString(json, "modified"); entry.modified = stringToTimePoint(modifiedStr); - + std::string expiresStr = SimpleJsonParser::extractString(json, "expires"); entry.expires = stringToTimePoint(expiresStr); - - std::string previousArray = SimpleJsonParser::extractArray(json, "previousPasswords"); + + std::string previousArray = + SimpleJsonParser::extractArray(json, "previousPasswords"); entry.previousPasswords = deserializeStringArray(previousArray); - + return Result(std::move(entry)); } -Result JsonSerializer::serializeEntries(const std::vector& entries) { +Result JsonSerializer::serializeEntries( + const std::vector& entries) { std::ostringstream json; - + json << "[\n"; for (size_t i = 0; i < entries.size(); ++i) { auto entryResult = serializeEntry(entries[i]); if (entryResult.isError()) { - return Result("Failed to serialize entry " + std::to_string(i) + - ": " + entryResult.error()); + return Result("Failed to serialize entry " + + std::to_string(i) + ": " + + entryResult.error()); } - + json << entryResult.value(); if (i < entries.size() - 1) { json << ","; @@ -82,30 +88,32 @@ Result JsonSerializer::serializeEntries(const std::vector(json.str()); } -Result> JsonSerializer::deserializeEntries(const std::string& json) { +Result> JsonSerializer::deserializeEntries( + const std::string& json) { if (!SimpleJsonParser::isValidJson(json)) { return Result>::error("Invalid JSON format"); } - + std::vector entries; - + // Simple array parsing - find individual objects size_t pos = json.find('['); if (pos == std::string::npos) { - return Result>::error("JSON is not an array"); + return Result>::error( + "JSON is not an array"); } - - pos++; // Skip opening bracket + + pos++; // Skip opening bracket int braceCount = 0; size_t objectStart = std::string::npos; - + for (size_t i = pos; i < json.length(); ++i) { char c = json[i]; - + if (c == '{') { if (braceCount == 0) { objectStart = i; @@ -115,7 +123,8 @@ Result> JsonSerializer::deserializeEntries(const std: braceCount--; if (braceCount == 0 && objectStart != std::string::npos) { // Extract object - std::string objectJson = json.substr(objectStart, i - objectStart + 1); + std::string objectJson = + json.substr(objectStart, i - objectStart + 1); auto entryResult = deserializeEntry(objectJson); if (entryResult.isError()) { return Result>::error( @@ -126,57 +135,81 @@ Result> JsonSerializer::deserializeEntries(const std: } } } - + return Result>(std::move(entries)); } -Result JsonSerializer::serializeSettings(const PasswordManagerSettings& settings) { +Result JsonSerializer::serializeSettings( + const PasswordManagerSettings& settings) { std::ostringstream json; - + json << "{\n"; - json << " \"autoLockTimeoutSeconds\": " << settings.autoLockTimeoutSeconds << ",\n"; - json << " \"notifyOnPasswordExpiry\": " << (settings.notifyOnPasswordExpiry ? "true" : "false") << ",\n"; - json << " \"passwordExpiryDays\": " << settings.passwordExpiryDays << ",\n"; + json << " \"autoLockTimeoutSeconds\": " << settings.autoLockTimeoutSeconds + << ",\n"; + json << " \"notifyOnPasswordExpiry\": " + << (settings.notifyOnPasswordExpiry ? "true" : "false") << ",\n"; + json << " \"passwordExpiryDays\": " << settings.passwordExpiryDays + << ",\n"; json << " \"minPasswordLength\": " << settings.minPasswordLength << ",\n"; - json << " \"requireSpecialChars\": " << (settings.requireSpecialChars ? "true" : "false") << ",\n"; - json << " \"requireNumbers\": " << (settings.requireNumbers ? "true" : "false") << ",\n"; - json << " \"requireMixedCase\": " << (settings.requireMixedCase ? "true" : "false") << ",\n"; + json << " \"requireSpecialChars\": " + << (settings.requireSpecialChars ? "true" : "false") << ",\n"; + json << " \"requireNumbers\": " + << (settings.requireNumbers ? "true" : "false") << ",\n"; + json << " \"requireMixedCase\": " + << (settings.requireMixedCase ? "true" : "false") << ",\n"; json << " \"encryptionOptions\": {\n"; - json << " \"useHardwareAcceleration\": " << (settings.encryptionOptions.useHardwareAcceleration ? "true" : "false") << ",\n"; - json << " \"keyIterations\": " << settings.encryptionOptions.keyIterations << ",\n"; - json << " \"encryptionMethod\": \"" << methodToString(settings.encryptionOptions.encryptionMethod) << "\"\n"; + json << " \"useHardwareAcceleration\": " + << (settings.encryptionOptions.useHardwareAcceleration ? "true" + : "false") + << ",\n"; + json << " \"keyIterations\": " + << settings.encryptionOptions.keyIterations << ",\n"; + json << " \"encryptionMethod\": \"" + << methodToString(settings.encryptionOptions.encryptionMethod) + << "\"\n"; json << " }\n"; json << "}"; - + return Result(json.str()); } -Result JsonSerializer::deserializeSettings(const std::string& json) { +Result JsonSerializer::deserializeSettings( + const std::string& json) { if (!SimpleJsonParser::isValidJson(json)) { return Result::error("Invalid JSON format"); } - + PasswordManagerSettings settings; - - settings.autoLockTimeoutSeconds = SimpleJsonParser::extractInt(json, "autoLockTimeoutSeconds"); - settings.notifyOnPasswordExpiry = SimpleJsonParser::extractBool(json, "notifyOnPasswordExpiry"); - settings.passwordExpiryDays = SimpleJsonParser::extractInt(json, "passwordExpiryDays"); - settings.minPasswordLength = SimpleJsonParser::extractInt(json, "minPasswordLength"); - settings.requireSpecialChars = SimpleJsonParser::extractBool(json, "requireSpecialChars"); - settings.requireNumbers = SimpleJsonParser::extractBool(json, "requireNumbers"); - settings.requireMixedCase = SimpleJsonParser::extractBool(json, "requireMixedCase"); - - std::string encryptionOptionsJson = SimpleJsonParser::extractObject(json, "encryptionOptions"); + + settings.autoLockTimeoutSeconds = + SimpleJsonParser::extractInt(json, "autoLockTimeoutSeconds"); + settings.notifyOnPasswordExpiry = + SimpleJsonParser::extractBool(json, "notifyOnPasswordExpiry"); + settings.passwordExpiryDays = + SimpleJsonParser::extractInt(json, "passwordExpiryDays"); + settings.minPasswordLength = + SimpleJsonParser::extractInt(json, "minPasswordLength"); + settings.requireSpecialChars = + SimpleJsonParser::extractBool(json, "requireSpecialChars"); + settings.requireNumbers = + SimpleJsonParser::extractBool(json, "requireNumbers"); + settings.requireMixedCase = + SimpleJsonParser::extractBool(json, "requireMixedCase"); + + std::string encryptionOptionsJson = + SimpleJsonParser::extractObject(json, "encryptionOptions"); if (!encryptionOptionsJson.empty()) { - settings.encryptionOptions.useHardwareAcceleration = - SimpleJsonParser::extractBool(encryptionOptionsJson, "useHardwareAcceleration"); - settings.encryptionOptions.keyIterations = - SimpleJsonParser::extractInt(encryptionOptionsJson, "keyIterations"); - - std::string methodStr = SimpleJsonParser::extractString(encryptionOptionsJson, "encryptionMethod"); + settings.encryptionOptions.useHardwareAcceleration = + SimpleJsonParser::extractBool(encryptionOptionsJson, + "useHardwareAcceleration"); + settings.encryptionOptions.keyIterations = SimpleJsonParser::extractInt( + encryptionOptionsJson, "keyIterations"); + + std::string methodStr = SimpleJsonParser::extractString( + encryptionOptionsJson, "encryptionMethod"); settings.encryptionOptions.encryptionMethod = stringToMethod(methodStr); } - + return Result(std::move(settings)); } @@ -186,42 +219,78 @@ std::string JsonSerializer::unescapeString(const std::string& str) { std::string JsonSerializer::escapeJsonString(const std::string& str) { std::ostringstream escaped; - + for (char c : str) { switch (c) { - case '"': escaped << "\\\""; break; - case '\\': escaped << "\\\\"; break; - case '\b': escaped << "\\b"; break; - case '\f': escaped << "\\f"; break; - case '\n': escaped << "\\n"; break; - case '\r': escaped << "\\r"; break; - case '\t': escaped << "\\t"; break; + case '"': + escaped << "\\\""; + break; + case '\\': + escaped << "\\\\"; + break; + case '\b': + escaped << "\\b"; + break; + case '\f': + escaped << "\\f"; + break; + case '\n': + escaped << "\\n"; + break; + case '\r': + escaped << "\\r"; + break; + case '\t': + escaped << "\\t"; + break; default: if (c < 0x20) { - escaped << "\\u" << std::hex << std::setw(4) << std::setfill('0') << static_cast(c); + escaped << "\\u" << std::hex << std::setw(4) + << std::setfill('0') << static_cast(c); } else { escaped << c; } break; } } - + return escaped.str(); } std::string JsonSerializer::unescapeJsonString(const std::string& str) { std::ostringstream unescaped; - + for (size_t i = 0; i < str.length(); ++i) { if (str[i] == '\\' && i + 1 < str.length()) { switch (str[i + 1]) { - case '"': unescaped << '"'; i++; break; - case '\\': unescaped << '\\'; i++; break; - case 'b': unescaped << '\b'; i++; break; - case 'f': unescaped << '\f'; i++; break; - case 'n': unescaped << '\n'; i++; break; - case 'r': unescaped << '\r'; i++; break; - case 't': unescaped << '\t'; i++; break; + case '"': + unescaped << '"'; + i++; + break; + case '\\': + unescaped << '\\'; + i++; + break; + case 'b': + unescaped << '\b'; + i++; + break; + case 'f': + unescaped << '\f'; + i++; + break; + case 'n': + unescaped << '\n'; + i++; + break; + case 'r': + unescaped << '\r'; + i++; + break; + case 't': + unescaped << '\t'; + i++; + break; case 'u': if (i + 5 < str.length()) { // Parse Unicode escape sequence @@ -241,14 +310,16 @@ std::string JsonSerializer::unescapeJsonString(const std::string& str) { unescaped << str[i]; } } - + return unescaped.str(); } -std::string JsonSerializer::timePointToString(const std::chrono::system_clock::time_point& timePoint) { +std::string JsonSerializer::timePointToString( + const std::chrono::system_clock::time_point& timePoint) { auto time_t = std::chrono::system_clock::to_time_t(timePoint); auto ms = std::chrono::duration_cast( - timePoint.time_since_epoch()) % 1000; + timePoint.time_since_epoch()) % + 1000; std::ostringstream oss; oss << std::put_time(std::gmtime(&time_t), "%Y-%m-%dT%H:%M:%S"); @@ -257,7 +328,8 @@ std::string JsonSerializer::timePointToString(const std::chrono::system_clock::t return oss.str(); } -std::chrono::system_clock::time_point JsonSerializer::stringToTimePoint(const std::string& timeString) { +std::chrono::system_clock::time_point JsonSerializer::stringToTimePoint( + const std::string& timeString) { if (timeString.empty()) { return std::chrono::system_clock::time_point{}; } @@ -277,7 +349,7 @@ std::chrono::system_clock::time_point JsonSerializer::stringToTimePoint(const st // Parse milliseconds if present if (ss.peek() == '.') { - ss.ignore(); // Skip the dot + ss.ignore(); // Skip the dot int ms = 0; ss >> ms; timePoint += std::chrono::milliseconds(ms); @@ -288,43 +360,65 @@ std::chrono::system_clock::time_point JsonSerializer::stringToTimePoint(const st std::string JsonSerializer::categoryToString(PasswordCategory category) { switch (category) { - case PasswordCategory::General: return "General"; - case PasswordCategory::Finance: return "Finance"; - case PasswordCategory::Work: return "Work"; - case PasswordCategory::Personal: return "Personal"; - case PasswordCategory::Social: return "Social"; - case PasswordCategory::Entertainment: return "Entertainment"; - case PasswordCategory::Other: return "Other"; - default: return "General"; + case PasswordCategory::General: + return "General"; + case PasswordCategory::Finance: + return "Finance"; + case PasswordCategory::Work: + return "Work"; + case PasswordCategory::Personal: + return "Personal"; + case PasswordCategory::Social: + return "Social"; + case PasswordCategory::Entertainment: + return "Entertainment"; + case PasswordCategory::Other: + return "Other"; + default: + return "General"; } } PasswordCategory JsonSerializer::stringToCategory(const std::string& str) { - if (str == "Finance") return PasswordCategory::Finance; - if (str == "Work") return PasswordCategory::Work; - if (str == "Personal") return PasswordCategory::Personal; - if (str == "Social") return PasswordCategory::Social; - if (str == "Entertainment") return PasswordCategory::Entertainment; - if (str == "Other") return PasswordCategory::Other; + if (str == "Finance") + return PasswordCategory::Finance; + if (str == "Work") + return PasswordCategory::Work; + if (str == "Personal") + return PasswordCategory::Personal; + if (str == "Social") + return PasswordCategory::Social; + if (str == "Entertainment") + return PasswordCategory::Entertainment; + if (str == "Other") + return PasswordCategory::Other; return PasswordCategory::General; } std::string JsonSerializer::methodToString(EncryptionOptions::Method method) { switch (method) { - case EncryptionOptions::Method::AES_GCM: return "AES_GCM"; - case EncryptionOptions::Method::AES_CBC: return "AES_CBC"; - case EncryptionOptions::Method::CHACHA20_POLY1305: return "CHACHA20_POLY1305"; - default: return "AES_GCM"; + case EncryptionOptions::Method::AES_GCM: + return "AES_GCM"; + case EncryptionOptions::Method::AES_CBC: + return "AES_CBC"; + case EncryptionOptions::Method::CHACHA20_POLY1305: + return "CHACHA20_POLY1305"; + default: + return "AES_GCM"; } } -EncryptionOptions::Method JsonSerializer::stringToMethod(const std::string& str) { - if (str == "AES_CBC") return EncryptionOptions::Method::AES_CBC; - if (str == "CHACHA20_POLY1305") return EncryptionOptions::Method::CHACHA20_POLY1305; +EncryptionOptions::Method JsonSerializer::stringToMethod( + const std::string& str) { + if (str == "AES_CBC") + return EncryptionOptions::Method::AES_CBC; + if (str == "CHACHA20_POLY1305") + return EncryptionOptions::Method::CHACHA20_POLY1305; return EncryptionOptions::Method::AES_GCM; } -std::string JsonSerializer::serializeStringArray(const std::vector& strings) { +std::string JsonSerializer::serializeStringArray( + const std::vector& strings) { std::ostringstream json; json << "["; @@ -339,7 +433,8 @@ std::string JsonSerializer::serializeStringArray(const std::vector& return json.str(); } -std::vector JsonSerializer::deserializeStringArray(const std::string& json) { +std::vector JsonSerializer::deserializeStringArray( + const std::string& json) { std::vector strings; if (json.empty() || json == "[]") { @@ -352,7 +447,7 @@ std::vector JsonSerializer::deserializeStringArray(const std::strin return strings; } - pos++; // Skip opening bracket + pos++; // Skip opening bracket bool inString = false; bool escaped = false; std::string currentString; @@ -388,7 +483,8 @@ std::vector JsonSerializer::deserializeStringArray(const std::strin // SimpleJsonParser Implementation // ============================================================================ -std::string SimpleJsonParser::extractString(const std::string& json, const std::string& key) { +std::string SimpleJsonParser::extractString(const std::string& json, + const std::string& key) { size_t endPos; size_t startPos = findValue(json, key, 0, endPos); @@ -399,14 +495,15 @@ std::string SimpleJsonParser::extractString(const std::string& json, const std:: // Skip opening quote if (json[startPos] == '"') { startPos++; - endPos--; // Skip closing quote + endPos--; // Skip closing quote } std::string value = json.substr(startPos, endPos - startPos); return JsonSerializer::unescapeString(value); } -int SimpleJsonParser::extractInt(const std::string& json, const std::string& key) { +int SimpleJsonParser::extractInt(const std::string& json, + const std::string& key) { size_t endPos; size_t startPos = findValue(json, key, 0, endPos); @@ -422,7 +519,8 @@ int SimpleJsonParser::extractInt(const std::string& json, const std::string& key } } -bool SimpleJsonParser::extractBool(const std::string& json, const std::string& key) { +bool SimpleJsonParser::extractBool(const std::string& json, + const std::string& key) { size_t endPos; size_t startPos = findValue(json, key, 0, endPos); @@ -434,7 +532,8 @@ bool SimpleJsonParser::extractBool(const std::string& json, const std::string& k return value == "true"; } -std::string SimpleJsonParser::extractArray(const std::string& json, const std::string& key) { +std::string SimpleJsonParser::extractArray(const std::string& json, + const std::string& key) { size_t endPos; size_t startPos = findValue(json, key, 0, endPos); @@ -445,7 +544,8 @@ std::string SimpleJsonParser::extractArray(const std::string& json, const std::s return json.substr(startPos, endPos - startPos); } -std::string SimpleJsonParser::extractObject(const std::string& json, const std::string& key) { +std::string SimpleJsonParser::extractObject(const std::string& json, + const std::string& key) { size_t endPos; size_t startPos = findValue(json, key, 0, endPos); @@ -484,18 +584,23 @@ bool SimpleJsonParser::isValidJson(const std::string& json) { } if (!inString) { - if (c == '{') braceCount++; - else if (c == '}') braceCount--; - else if (c == '[') bracketCount++; - else if (c == ']') bracketCount--; + if (c == '{') + braceCount++; + else if (c == '}') + braceCount--; + else if (c == '[') + bracketCount++; + else if (c == ']') + bracketCount--; } } return braceCount == 0 && bracketCount == 0 && !inString; } -size_t SimpleJsonParser::findValue(const std::string& json, const std::string& key, - size_t startPos, size_t& endPos) { +size_t SimpleJsonParser::findValue(const std::string& json, + const std::string& key, size_t startPos, + size_t& endPos) { std::string searchKey = "\"" + key + "\""; size_t keyPos = json.find(searchKey, startPos); @@ -528,7 +633,8 @@ size_t SimpleJsonParser::skipWhitespace(const std::string& json, size_t pos) { return pos; } -size_t SimpleJsonParser::findValueEnd(const std::string& json, size_t startPos) { +size_t SimpleJsonParser::findValueEnd(const std::string& json, + size_t startPos) { if (startPos >= json.length()) { return startPos; } @@ -572,7 +678,8 @@ size_t SimpleJsonParser::findValueEnd(const std::string& json, size_t startPos) continue; } if (!inString) { - if (json[i] == '{') braceCount++; + if (json[i] == '{') + braceCount++; else if (json[i] == '}') { braceCount--; if (braceCount == 0) { @@ -602,7 +709,8 @@ size_t SimpleJsonParser::findValueEnd(const std::string& json, size_t startPos) continue; } if (!inString) { - if (json[i] == '[') bracketCount++; + if (json[i] == '[') + bracketCount++; else if (json[i] == ']') { bracketCount--; if (bracketCount == 0) { diff --git a/atom/secret/serialization.hpp b/atom/secret/serialization.hpp index 7035ba62..15d86b93 100644 --- a/atom/secret/serialization.hpp +++ b/atom/secret/serialization.hpp @@ -1,9 +1,9 @@ #ifndef ATOM_SECRET_SERIALIZATION_HPP #define ATOM_SECRET_SERIALIZATION_HPP +#include #include #include -#include #include "password_entry.hpp" #include "result.hpp" @@ -34,28 +34,32 @@ class JsonSerializer { * @param entries Vector of password entries to serialize. * @return Result containing JSON string or error message. */ - static Result serializeEntries(const std::vector& entries); + static Result serializeEntries( + const std::vector& entries); /** * @brief Deserializes multiple PasswordEntry objects from JSON array. * @param json The JSON array string to deserialize. * @return Result containing vector of PasswordEntry or error message. */ - static Result> deserializeEntries(const std::string& json); + static Result> deserializeEntries( + const std::string& json); /** * @brief Serializes a PasswordManagerSettings object to JSON. * @param settings The settings to serialize. * @return Result containing JSON string or error message. */ - static Result serializeSettings(const PasswordManagerSettings& settings); + static Result serializeSettings( + const PasswordManagerSettings& settings); /** * @brief Deserializes a PasswordManagerSettings object from JSON. * @param json The JSON string to deserialize. * @return Result containing PasswordManagerSettings or error message. */ - static Result deserializeSettings(const std::string& json); + static Result deserializeSettings( + const std::string& json); /** * @brief Unescapes a JSON string (public utility). @@ -84,14 +88,16 @@ class JsonSerializer { * @param timePoint Time point to convert. * @return ISO 8601 formatted string. */ - static std::string timePointToString(const std::chrono::system_clock::time_point& timePoint); + static std::string timePointToString( + const std::chrono::system_clock::time_point& timePoint); /** * @brief Converts ISO 8601 string to time_point. * @param timeString ISO 8601 formatted string. * @return Time point. */ - static std::chrono::system_clock::time_point stringToTimePoint(const std::string& timeString); + static std::chrono::system_clock::time_point stringToTimePoint( + const std::string& timeString); /** * @brief Converts PasswordCategory enum to string. @@ -126,14 +132,16 @@ class JsonSerializer { * @param strings Vector of strings to serialize. * @return JSON array string. */ - static std::string serializeStringArray(const std::vector& strings); + static std::string serializeStringArray( + const std::vector& strings); /** * @brief Deserializes a JSON array to vector of strings. * @param json JSON array string. * @return Vector of strings. */ - static std::vector deserializeStringArray(const std::string& json); + static std::vector deserializeStringArray( + const std::string& json); }; /** @@ -148,7 +156,8 @@ class SimpleJsonParser { * @param key Key to extract. * @return Extracted string value or empty string if not found. */ - static std::string extractString(const std::string& json, const std::string& key); + static std::string extractString(const std::string& json, + const std::string& key); /** * @brief Extracts an integer value from JSON. @@ -172,7 +181,8 @@ class SimpleJsonParser { * @param key Key to extract. * @return Extracted array as string or empty string if not found. */ - static std::string extractArray(const std::string& json, const std::string& key); + static std::string extractArray(const std::string& json, + const std::string& key); /** * @brief Extracts an object value from JSON. @@ -180,7 +190,8 @@ class SimpleJsonParser { * @param key Key to extract. * @return Extracted object as string or empty string if not found. */ - static std::string extractObject(const std::string& json, const std::string& key); + static std::string extractObject(const std::string& json, + const std::string& key); /** * @brief Checks if JSON string is valid. @@ -198,8 +209,8 @@ class SimpleJsonParser { * @param endPos Ending position of the value. * @return Starting position of the value or std::string::npos if not found. */ - static size_t findValue(const std::string& json, const std::string& key, - size_t startPos, size_t& endPos); + static size_t findValue(const std::string& json, const std::string& key, + size_t startPos, size_t& endPos); /** * @brief Skips whitespace characters. diff --git a/atom/serial/CMakeLists.txt b/atom/serial/CMakeLists.txt index 8931f203..b6c512e8 100644 --- a/atom/serial/CMakeLists.txt +++ b/atom/serial/CMakeLists.txt @@ -1,10 +1,11 @@ -# CMakeLists.txt for Serial Module -# Part of the Atom Project -# Author: Max Qian +# CMakeLists.txt for Serial Module Part of the Atom Project Author: Max Qian # License: GPL3 cmake_minimum_required(VERSION 3.21) -project(atom-serial VERSION 1.0.0 LANGUAGES C CXX) +project( + atom-serial + VERSION 1.0.0 + LANGUAGES C CXX) # Include standardized module configuration include(${CMAKE_SOURCE_DIR}/cmake/ModuleDependencies.cmake) @@ -15,30 +16,27 @@ set(LIB_NAME atom-serial) # Check for USB support find_package(PkgConfig QUIET) if(PkgConfig_FOUND) - pkg_check_modules(LIBUSB QUIET libusb-1.0) + pkg_check_modules(LIBUSB QUIET libusb-1.0) endif() # Sources set(SOURCES # Core files - core/scanner.cpp - core/serial_port.cpp - + core/scanner.cpp core/serial_port.cpp # Bluetooth files - bluetooth/bluetooth_serial.cpp -) + bluetooth/bluetooth_serial.cpp) # Add USB files only if libusb is available if(LIBUSB_FOUND) - list(APPEND SOURCES usb/usb.cpp) - message(STATUS "USB support enabled for atom-serial") + list(APPEND SOURCES usb/usb.cpp) + message(STATUS "USB support enabled for atom-serial") else() - message(STATUS "USB support disabled for atom-serial (libusb not found)") + message(STATUS "USB support disabled for atom-serial (libusb not found)") endif() # Add platform-specific sources if(APPLE) - list(APPEND SOURCES bluetooth/bluetooth_serial_mac.mm) + list(APPEND SOURCES bluetooth/bluetooth_serial_mac.mm) endif() # Headers @@ -55,19 +53,16 @@ set(HEADERS # Actual implementation headers (in subdirectories) core/scanner.hpp core/serial_port.hpp - bluetooth/bluetooth_serial.hpp bluetooth/bluetooth_serial_mac.hpp bluetooth/bluetooth_serial_unix.hpp bluetooth/bluetooth_serial_win.hpp - platform/serial_port_unix.hpp - platform/serial_port_win.hpp -) + platform/serial_port_win.hpp) # Add USB headers only if libusb is available if(LIBUSB_FOUND) - list(APPEND HEADERS usb.hpp usb/usb.hpp) + list(APPEND HEADERS usb.hpp usb/usb.hpp) endif() # Create library target @@ -78,63 +73,46 @@ atom_configure_module(${LIB_NAME}) # Set platform-specific dependencies if(WIN32) - target_link_libraries(${LIB_NAME} - PUBLIC - atom-error - atom-log - SetupAPI - Cfgmgr32 - ) + target_link_libraries(${LIB_NAME} PUBLIC atom-error atom-log SetupAPI + Cfgmgr32) elseif(APPLE) - find_library(IOKIT_FRAMEWORK IOKit REQUIRED) - find_library(FOUNDATION_FRAMEWORK Foundation REQUIRED) - target_link_libraries(${LIB_NAME} - PUBLIC - atom-error - atom-log - ${IOKIT_FRAMEWORK} - ${FOUNDATION_FRAMEWORK} - ) + find_library(IOKIT_FRAMEWORK IOKit REQUIRED) + find_library(FOUNDATION_FRAMEWORK Foundation REQUIRED) + target_link_libraries( + ${LIB_NAME} PUBLIC atom-error atom-log ${IOKIT_FRAMEWORK} + ${FOUNDATION_FRAMEWORK}) else() # Linux/Unix - find_package(PkgConfig REQUIRED) - pkg_check_modules(UDEV REQUIRED libudev) - - target_include_directories(${LIB_NAME} PUBLIC - ${UDEV_INCLUDE_DIRS} - ) - - target_link_libraries(${LIB_NAME} - PUBLIC - atom-error - atom-log - ${UDEV_LIBRARIES} - ) - - # Add USB support if available - if(LIBUSB_FOUND) - target_include_directories(${LIB_NAME} PUBLIC ${LIBUSB_INCLUDE_DIRS}) - target_link_libraries(${LIB_NAME} PUBLIC ${LIBUSB_LIBRARIES}) - target_compile_definitions(${LIB_NAME} PUBLIC ATOM_SERIAL_HAS_USB) - endif() + find_package(PkgConfig REQUIRED) + pkg_check_modules(UDEV REQUIRED libudev) + + target_include_directories(${LIB_NAME} PUBLIC ${UDEV_INCLUDE_DIRS}) + + target_link_libraries(${LIB_NAME} PUBLIC atom-error atom-log + ${UDEV_LIBRARIES}) + + # Add USB support if available + if(LIBUSB_FOUND) + target_include_directories(${LIB_NAME} PUBLIC ${LIBUSB_INCLUDE_DIRS}) + target_link_libraries(${LIB_NAME} PUBLIC ${LIBUSB_LIBRARIES}) + target_compile_definitions(${LIB_NAME} PUBLIC ATOM_SERIAL_HAS_USB) + endif() endif() # Add Bluetooth support if available if(WIN32) - target_link_libraries(${LIB_NAME} PUBLIC BluetoothApis) + target_link_libraries(${LIB_NAME} PUBLIC BluetoothApis) elseif(APPLE) - # macOS Bluetooth support is already via IOKit and Foundation + # macOS Bluetooth support is already via IOKit and Foundation elseif(UNIX) - pkg_check_modules(BLUEZ QUIET bluez) - if(BLUEZ_FOUND) - target_include_directories(${LIB_NAME} PUBLIC ${BLUEZ_INCLUDE_DIRS}) - target_link_libraries(${LIB_NAME} PUBLIC ${BLUEZ_LIBRARIES}) - target_compile_definitions(${LIB_NAME} PUBLIC HAVE_BLUEZ) - endif() + pkg_check_modules(BLUEZ QUIET bluez) + if(BLUEZ_FOUND) + target_include_directories(${LIB_NAME} PUBLIC ${BLUEZ_INCLUDE_DIRS}) + target_link_libraries(${LIB_NAME} PUBLIC ${BLUEZ_LIBRARIES}) + target_compile_definitions(${LIB_NAME} PUBLIC HAVE_BLUEZ) + endif() endif() # Install headers -install(FILES ${HEADERS} - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/atom/serial -) +install(FILES ${HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/atom/serial) message(STATUS "Serial module configured") diff --git a/atom/serial/bluetooth/bluetooth_serial_win.hpp b/atom/serial/bluetooth/bluetooth_serial_win.hpp index 1527f672..d72e37ce 100644 --- a/atom/serial/bluetooth/bluetooth_serial_win.hpp +++ b/atom/serial/bluetooth/bluetooth_serial_win.hpp @@ -129,71 +129,72 @@ class BluetoothSerialImpl { } stopScan_ = false; - scanThread_ = std::thread([this, onDeviceFound, onScanComplete, - timeout]() { - std::unordered_set discoveredAddresses; - - BLUETOOTH_DEVICE_SEARCH_PARAMS searchParams = {}; - searchParams.dwSize = sizeof(BLUETOOTH_DEVICE_SEARCH_PARAMS); - searchParams.fReturnAuthenticated = 1; - searchParams.fReturnRemembered = 1; - searchParams.fReturnUnknown = 1; - searchParams.fReturnConnected = 1; - searchParams.fIssueInquiry = 1; - searchParams.cTimeoutMultiplier = static_cast(timeout.count()); - - BLUETOOTH_DEVICE_INFO deviceInfo = {}; - deviceInfo.dwSize = sizeof(BLUETOOTH_DEVICE_INFO); - - auto startTime = std::chrono::steady_clock::now(); - - while (!stopScan_ && - (std::chrono::steady_clock::now() - startTime) < timeout) { - HBLUETOOTH_DEVICE_FIND hFind = - BluetoothFindFirstDevice(&searchParams, &deviceInfo); - if (hFind) { - do { - if (stopScan_) - break; - - BluetoothDeviceInfo info; - char addressStr[18] = {0}; - sprintf_s(addressStr, sizeof(addressStr), - "%02X:%02X:%02X:%02X:%02X:%02X", - deviceInfo.Address.rgBytes[5], - deviceInfo.Address.rgBytes[4], - deviceInfo.Address.rgBytes[3], - deviceInfo.Address.rgBytes[2], - deviceInfo.Address.rgBytes[1], - deviceInfo.Address.rgBytes[0]); - info.address = addressStr; - - if (discoveredAddresses.find(info.address) == - discoveredAddresses.end()) { - char narrowName[248] = {0}; - wcstombs(narrowName, deviceInfo.szName, 248); - info.name = narrowName; - - info.paired = (deviceInfo.fAuthenticated != 0); - info.connected = (deviceInfo.fConnected != 0); - info.rssi = 0; - - discoveredAddresses.insert(info.address); - onDeviceFound(info); - } - } while (!stopScan_ && - BluetoothFindNextDevice(hFind, &deviceInfo)); + scanThread_ = + std::thread([this, onDeviceFound, onScanComplete, timeout]() { + std::unordered_set discoveredAddresses; + + BLUETOOTH_DEVICE_SEARCH_PARAMS searchParams = {}; + searchParams.dwSize = sizeof(BLUETOOTH_DEVICE_SEARCH_PARAMS); + searchParams.fReturnAuthenticated = 1; + searchParams.fReturnRemembered = 1; + searchParams.fReturnUnknown = 1; + searchParams.fReturnConnected = 1; + searchParams.fIssueInquiry = 1; + searchParams.cTimeoutMultiplier = + static_cast(timeout.count()); + + BLUETOOTH_DEVICE_INFO deviceInfo = {}; + deviceInfo.dwSize = sizeof(BLUETOOTH_DEVICE_INFO); + + auto startTime = std::chrono::steady_clock::now(); + + while (!stopScan_ && (std::chrono::steady_clock::now() - + startTime) < timeout) { + HBLUETOOTH_DEVICE_FIND hFind = + BluetoothFindFirstDevice(&searchParams, &deviceInfo); + if (hFind) { + do { + if (stopScan_) + break; + + BluetoothDeviceInfo info; + char addressStr[18] = {0}; + sprintf_s(addressStr, sizeof(addressStr), + "%02X:%02X:%02X:%02X:%02X:%02X", + deviceInfo.Address.rgBytes[5], + deviceInfo.Address.rgBytes[4], + deviceInfo.Address.rgBytes[3], + deviceInfo.Address.rgBytes[2], + deviceInfo.Address.rgBytes[1], + deviceInfo.Address.rgBytes[0]); + info.address = addressStr; + + if (discoveredAddresses.find(info.address) == + discoveredAddresses.end()) { + char narrowName[248] = {0}; + wcstombs(narrowName, deviceInfo.szName, 248); + info.name = narrowName; + + info.paired = (deviceInfo.fAuthenticated != 0); + info.connected = (deviceInfo.fConnected != 0); + info.rssi = 0; + + discoveredAddresses.insert(info.address); + onDeviceFound(info); + } + } while (!stopScan_ && + BluetoothFindNextDevice(hFind, &deviceInfo)); + + BluetoothFindDeviceClose(hFind); + } - BluetoothFindDeviceClose(hFind); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); } - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - } - - if (!stopScan_) { - onScanComplete(); - } - }); + if (!stopScan_) { + onScanComplete(); + } + }); } void stopScan() { diff --git a/atom/sysinfo/hardware/battery.cpp b/atom/sysinfo/hardware/battery.cpp index 4ec7c398..79a6d521 100644 --- a/atom/sysinfo/hardware/battery.cpp +++ b/atom/sysinfo/hardware/battery.cpp @@ -603,8 +603,9 @@ class BatteryManager::BatteryManagerImpl { BatteryMonitor::stopMonitoring(); } - [[nodiscard]] auto getHistory(unsigned int maxEntries) const -> std::vector< - std::pair> { + [[nodiscard]] auto getHistory(unsigned int maxEntries) const + -> std::vector< + std::pair> { std::shared_lock lock(m_mutex); if (maxEntries == 0 || maxEntries >= m_historyData.size()) { @@ -786,8 +787,9 @@ auto BatteryManager::startMonitoring(unsigned int interval_ms) -> bool { void BatteryManager::stopMonitoring() { impl->stopMonitoring(); } -auto BatteryManager::getHistory(unsigned int maxEntries) const -> std::vector< - std::pair> { +auto BatteryManager::getHistory(unsigned int maxEntries) const + -> std::vector< + std::pair> { return impl->getHistory(maxEntries); } diff --git a/atom/sysinfo/hardware/bios.cpp b/atom/sysinfo/hardware/bios.cpp index fd6e724f..25484f2c 100644 --- a/atom/sysinfo/hardware/bios.cpp +++ b/atom/sysinfo/hardware/bios.cpp @@ -11,13 +11,17 @@ // Helper function to convert BSTR to std::string (MinGW compatible) static std::string BSTRToString(BSTR bstr) { - if (!bstr) return ""; + if (!bstr) + return ""; - int len = WideCharToMultiByte(CP_UTF8, 0, bstr, -1, nullptr, 0, nullptr, nullptr); - if (len <= 0) return ""; + int len = + WideCharToMultiByte(CP_UTF8, 0, bstr, -1, nullptr, 0, nullptr, nullptr); + if (len <= 0) + return ""; std::string result(len - 1, '\0'); - WideCharToMultiByte(CP_UTF8, 0, bstr, -1, &result[0], len, nullptr, nullptr); + WideCharToMultiByte(CP_UTF8, 0, bstr, -1, &result[0], len, nullptr, + nullptr); return result; } @@ -25,7 +29,10 @@ static std::string BSTRToString(BSTR bstr) { class BSTRWrapper { public: explicit BSTRWrapper(const wchar_t* str) : bstr_(SysAllocString(str)) {} - ~BSTRWrapper() { if (bstr_) SysFreeString(bstr_); } + ~BSTRWrapper() { + if (bstr_) + SysFreeString(bstr_); + } BSTRWrapper(const BSTRWrapper&) = delete; BSTRWrapper& operator=(const BSTRWrapper&) = delete; @@ -191,8 +198,8 @@ BiosHealthStatus BiosInfo::checkHealth() const { } BSTRWrapper rootCimv2(L"ROOT\\CIMV2"); - hres = pLoc->ConnectServer(rootCimv2, nullptr, nullptr, 0, - 0, 0, 0, pSvc.getAddressOf()); + hres = pLoc->ConnectServer(rootCimv2, nullptr, nullptr, 0, 0, 0, 0, + pSvc.getAddressOf()); if (FAILED(hres)) { throw std::runtime_error("Could not connect to WMI namespace"); } @@ -208,19 +215,20 @@ BiosHealthStatus BiosInfo::checkHealth() const { ComPtr pEnumerator; BSTRWrapper wql2(L"WQL"); - BSTRWrapper query2(L"SELECT * FROM Win32_NTLogEvent WHERE LogFile='System' AND " - L"EventCode='7' AND SourceName='Microsoft-Windows-BIOS' AND " - L"TimeWritten > '20230101000000.000000-000'"); + BSTRWrapper query2( + L"SELECT * FROM Win32_NTLogEvent WHERE LogFile='System' AND " + L"EventCode='7' AND SourceName='Microsoft-Windows-BIOS' AND " + L"TimeWritten > '20230101000000.000000-000'"); hres = pSvc->ExecQuery( - wql2, query2, - WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, nullptr, - pEnumerator.getAddressOf()); + wql2, query2, WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, + nullptr, pEnumerator.getAddressOf()); if (SUCCEEDED(hres)) { ComPtr pclsObj; ULONG uReturn = 0; - while (pEnumerator->Next(static_cast(WBEM_INFINITE), 1, pclsObj.getAddressOf(), + while (pEnumerator->Next(static_cast(WBEM_INFINITE), 1, + pclsObj.getAddressOf(), &uReturn) == S_OK) { if (uReturn == 0) break; @@ -230,8 +238,7 @@ BiosHealthStatus BiosInfo::checkHealth() const { if (SUCCEEDED(pclsObj->Get(L"Message", 0, &vtProp, 0, 0))) { status.isHealthy = false; - status.errors.push_back( - BSTRToString(vtProp.bstrVal)); + status.errors.push_back(BSTRToString(vtProp.bstrVal)); } VariantClear(&vtProp); @@ -249,7 +256,8 @@ BiosHealthStatus BiosInfo::checkHealth() const { currentTime - biosTime) .count(); // Clamp to reasonable range to avoid overflow - status.biosAgeInDays = static_cast(std::min(biosAge, static_cast(INT_MAX))); + status.biosAgeInDays = static_cast( + std::min(biosAge, static_cast(INT_MAX))); if (biosAge > 730) { status.warnings.push_back( @@ -322,7 +330,8 @@ BiosHealthStatus BiosInfo::checkHealth() const { .count() / 24; // Clamp to reasonable range to avoid overflow - status.biosAgeInDays = static_cast(std::min(biosAge, static_cast(INT_MAX))); + status.biosAgeInDays = static_cast( + std::min(biosAge, static_cast(INT_MAX))); if (biosAge > 730) { status.warnings.push_back( @@ -551,8 +560,8 @@ BiosInfoData BiosInfo::fetchBiosInfo() { } BSTRWrapper rootCimv2_2(L"ROOT\\CIMV2"); - hres = pLoc->ConnectServer(rootCimv2_2, nullptr, nullptr, 0, - 0, 0, 0, pSvc.getAddressOf()); + hres = pLoc->ConnectServer(rootCimv2_2, nullptr, nullptr, 0, 0, 0, 0, + pSvc.getAddressOf()); if (FAILED(hres)) { throw std::runtime_error("Could not connect to WMI namespace"); } @@ -593,10 +602,9 @@ BiosInfoData BiosInfo::fetchBiosInfo() { VariantInit(&vtProp); if (SUCCEEDED( pclsObj->Get(prop, 0, &vtProp, nullptr, nullptr))) { - std::string result = - (vtProp.vt == VT_BSTR) - ? BSTRToString(vtProp.bstrVal) - : ""; + std::string result = (vtProp.vt == VT_BSTR) + ? BSTRToString(vtProp.bstrVal) + : ""; VariantClear(&vtProp); return result; } @@ -686,8 +694,8 @@ bool BiosInfo::isUEFIBootSupported() { return false; BSTRWrapper rootWmi(L"ROOT\\WMI"); - hres = pLoc->ConnectServer(rootWmi, nullptr, nullptr, 0, - 0, 0, 0, pSvc.getAddressOf()); + hres = pLoc->ConnectServer(rootWmi, nullptr, nullptr, 0, 0, 0, 0, + pSvc.getAddressOf()); if (FAILED(hres) || !pSvc.get()) return false; @@ -695,9 +703,8 @@ bool BiosInfo::isUEFIBootSupported() { BSTRWrapper wql3(L"WQL"); BSTRWrapper query3(L"SELECT * FROM MSFirmwareUefiInfo"); hres = pSvc->ExecQuery( - wql3, query3, - WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, nullptr, - pEnumerator.getAddressOf()); + wql3, query3, WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, + nullptr, pEnumerator.getAddressOf()); return SUCCEEDED(hres) && pEnumerator.get(); } catch (...) { diff --git a/atom/sysinfo/hardware/cpu/freebsd.cpp b/atom/sysinfo/hardware/cpu/freebsd.cpp index 2931a59c..982facd6 100644 --- a/atom/sysinfo/hardware/cpu/freebsd.cpp +++ b/atom/sysinfo/hardware/cpu/freebsd.cpp @@ -14,8 +14,8 @@ Description: System Information Module - CPU FreeBSD Implementation #ifdef __FreeBSD__ -#include "common.hpp" #include +#include "common.hpp" namespace atom::system { @@ -28,7 +28,7 @@ auto getCPUModel_FreeBSD() -> std::string; // 这里应该添加所有函数的前向声明 auto getCurrentCpuUsage_FreeBSD() -> float { - spdlog::info( "Starting getCurrentCpuUsage function on FreeBSD"); + spdlog::info("Starting getCurrentCpuUsage function on FreeBSD"); static std::mutex mutex; static long lastTotal = 0, lastIdle = 0; @@ -41,7 +41,8 @@ auto getCurrentCpuUsage_FreeBSD() -> float { size_t len = sizeof(cp_time); if (sysctlbyname("kern.cp_time", &cp_time, &len, NULL, 0) != -1) { - long total = cp_time[CP_USER] + cp_time[CP_NICE] + cp_time[CP_SYS] + cp_time[CP_IDLE] + cp_time[CP_INTR]; + long total = cp_time[CP_USER] + cp_time[CP_NICE] + cp_time[CP_SYS] + + cp_time[CP_IDLE] + cp_time[CP_INTR]; long idle = cp_time[CP_IDLE]; if (lastTotal > 0 && lastIdle > 0) { @@ -49,7 +50,8 @@ auto getCurrentCpuUsage_FreeBSD() -> float { long idleDiff = idle - lastIdle; if (totalDiff > 0) { - cpuUsage = 100.0f * (1.0f - (static_cast(idleDiff) / totalDiff)); + cpuUsage = 100.0f * + (1.0f - (static_cast(idleDiff) / totalDiff)); } } @@ -60,12 +62,12 @@ auto getCurrentCpuUsage_FreeBSD() -> float { // Clamp to 0-100 range cpuUsage = std::max(0.0f, std::min(100.0f, cpuUsage)); - spdlog::info( "FreeBSD CPU Usage: {}%", cpuUsage); + spdlog::info("FreeBSD CPU Usage: {}%", cpuUsage); return cpuUsage; } auto getPerCoreCpuUsage() -> std::vector { - spdlog::info( "Starting getPerCoreCpuUsage function on FreeBSD"); + spdlog::info("Starting getPerCoreCpuUsage function on FreeBSD"); static std::mutex mutex; static std::vector lastTotals; @@ -90,11 +92,13 @@ auto getPerCoreCpuUsage() -> std::vector { std::string sysctlName = "kern.cp_times"; if (sysctlbyname(sysctlName.c_str(), NULL, &len, NULL, 0) != -1) { std::vector times(len / sizeof(long)); - if (sysctlbyname(sysctlName.c_str(), times.data(), &len, NULL, 0) != -1) { + if (sysctlbyname(sysctlName.c_str(), times.data(), &len, NULL, 0) != + -1) { // Each CPU has CPUSTATES values int j = i * CPUSTATES; - long total = times[j + CP_USER] + times[j + CP_NICE] + times[j + CP_SYS] + - times[j + CP_IDLE] + times[j + CP_INTR]; + long total = times[j + CP_USER] + times[j + CP_NICE] + + times[j + CP_SYS] + times[j + CP_IDLE] + + times[j + CP_INTR]; long idle = times[j + CP_IDLE]; if (lastTotals[i] > 0 && lastIdles[i] > 0) { @@ -102,8 +106,11 @@ auto getPerCoreCpuUsage() -> std::vector { long idleDiff = idle - lastIdles[i]; if (totalDiff > 0) { - coreUsages[i] = 100.0f * (1.0f - (static_cast(idleDiff) / totalDiff)); - coreUsages[i] = std::max(0.0f, std::min(100.0f, coreUsages[i])); + coreUsages[i] = + 100.0f * + (1.0f - (static_cast(idleDiff) / totalDiff)); + coreUsages[i] = + std::max(0.0f, std::min(100.0f, coreUsages[i])); } } @@ -113,7 +120,7 @@ auto getPerCoreCpuUsage() -> std::vector { } } - spdlog::info( "FreeBSD Per-Core CPU Usage collected for {} cores", numCpus); + spdlog::info("FreeBSD Per-Core CPU Usage collected for {} cores", numCpus); return coreUsages; } @@ -128,37 +135,45 @@ auto getCurrentCpuTemperature() -> float { size_t len = sizeof(temp_value); // Try hw.acpi.thermal.tz0.temperature first - if (sysctlbyname("hw.acpi.thermal.tz0.temperature", &temp_value, &len, nullptr, 0) == 0) { + if (sysctlbyname("hw.acpi.thermal.tz0.temperature", &temp_value, &len, + nullptr, 0) == 0) { // Temperature is in tenths of Kelvin, convert to Celsius temperature = (temp_value / 10.0f) - 273.15f; found = true; - spdlog::debug("FreeBSD CPU Temperature from ACPI thermal zone: {}°C", temperature); + spdlog::debug("FreeBSD CPU Temperature from ACPI thermal zone: {}°C", + temperature); } // Try dev.cpu.0.temperature (requires coretemp module) if (!found) { len = sizeof(temp_value); - if (sysctlbyname("dev.cpu.0.temperature", &temp_value, &len, nullptr, 0) == 0) { + if (sysctlbyname("dev.cpu.0.temperature", &temp_value, &len, nullptr, + 0) == 0) { // Temperature is in tenths of Kelvin, convert to Celsius temperature = (temp_value / 10.0f) - 273.15f; found = true; - spdlog::debug("FreeBSD CPU Temperature from dev.cpu.0: {}°C", temperature); + spdlog::debug("FreeBSD CPU Temperature from dev.cpu.0: {}°C", + temperature); } } // Try dev.pchtherm.0.temperature as fallback if (!found) { len = sizeof(temp_value); - if (sysctlbyname("dev.pchtherm.0.temperature", &temp_value, &len, nullptr, 0) == 0) { + if (sysctlbyname("dev.pchtherm.0.temperature", &temp_value, &len, + nullptr, 0) == 0) { // Temperature is in tenths of Kelvin, convert to Celsius temperature = (temp_value / 10.0f) - 273.15f; found = true; - spdlog::debug("FreeBSD CPU Temperature from PCH thermal: {}°C", temperature); + spdlog::debug("FreeBSD CPU Temperature from PCH thermal: {}°C", + temperature); } } if (!found) { - spdlog::warn("FreeBSD: No CPU temperature sensor found. Ensure coretemp module is loaded or ACPI thermal zones are available."); + spdlog::warn( + "FreeBSD: No CPU temperature sensor found. Ensure coretemp module " + "is loaded or ACPI thermal zones are available."); } else { spdlog::info("FreeBSD CPU Temperature: {}°C", temperature); } @@ -175,34 +190,42 @@ auto getPerCoreCpuTemperature() -> std::vector { // Try to read per-core temperatures from dev.cpu.X.temperature for (int i = 0; i < numCores; ++i) { - std::string sysctl_name = "dev.cpu." + std::to_string(i) + ".temperature"; + std::string sysctl_name = + "dev.cpu." + std::to_string(i) + ".temperature"; int temp_value = 0; size_t len = sizeof(temp_value); - if (sysctlbyname(sysctl_name.c_str(), &temp_value, &len, nullptr, 0) == 0) { + if (sysctlbyname(sysctl_name.c_str(), &temp_value, &len, nullptr, 0) == + 0) { // Temperature is in tenths of Kelvin, convert to Celsius temperatures[i] = (temp_value / 10.0f) - 273.15f; foundCount++; - spdlog::debug("FreeBSD Core {} Temperature: {}°C", i, temperatures[i]); + spdlog::debug("FreeBSD Core {} Temperature: {}°C", i, + temperatures[i]); } else { spdlog::debug("FreeBSD: Could not read temperature for core {}", i); } } if (foundCount == 0) { - spdlog::warn("FreeBSD: No per-core temperature sensors found. Ensure coretemp module is loaded."); + spdlog::warn( + "FreeBSD: No per-core temperature sensors found. Ensure coretemp " + "module is loaded."); // Fallback: use overall CPU temperature for all cores float overallTemp = getCurrentCpuTemperature(); std::fill(temperatures.begin(), temperatures.end(), overallTemp); } else { - spdlog::info("FreeBSD Per-Core CPU Temperature: Retrieved {} of {} core temperatures", foundCount, numCores); + spdlog::info( + "FreeBSD Per-Core CPU Temperature: Retrieved {} of {} core " + "temperatures", + foundCount, numCores); } return temperatures; } auto getCPUModel() -> std::string { - spdlog::info( "Starting getCPUModel function on FreeBSD"); + spdlog::info("Starting getCPUModel function on FreeBSD"); if (!needsCacheRefresh() && !g_cpuInfoCache.model.empty()) { return g_cpuInfoCache.model; @@ -218,12 +241,12 @@ auto getCPUModel() -> std::string { cpuModel = buffer; } - spdlog::info( "FreeBSD CPU Model: {}", cpuModel); + spdlog::info("FreeBSD CPU Model: {}", cpuModel); return cpuModel; } auto getProcessorIdentifier() -> std::string { - spdlog::info( "Starting getProcessorIdentifier function on FreeBSD"); + spdlog::info("Starting getProcessorIdentifier function on FreeBSD"); if (!needsCacheRefresh() && !g_cpuInfoCache.identifier.empty()) { return g_cpuInfoCache.identifier; @@ -265,12 +288,12 @@ auto getProcessorIdentifier() -> std::string { identifier = "FreeBSD CPU"; } - spdlog::info( "FreeBSD CPU Identifier: {}", identifier); + spdlog::info("FreeBSD CPU Identifier: {}", identifier); return identifier; } auto getProcessorFrequency() -> double { - spdlog::info( "Starting getProcessorFrequency function on FreeBSD"); + spdlog::info("Starting getProcessorFrequency function on FreeBSD"); double frequency = 0.0; @@ -280,20 +303,21 @@ auto getProcessorFrequency() -> double { if (sysctlbyname("dev.cpu.0.freq", &freq, &len, NULL, 0) != -1) { // dev.cpu.0.freq returns frequency in MHz - frequency = static_cast(freq) / 1000.0; // Convert MHz to GHz + frequency = static_cast(freq) / 1000.0; // Convert MHz to GHz } else { // Alternative: try hw.clockrate if (sysctlbyname("hw.clockrate", &freq, &len, NULL, 0) != -1) { - frequency = static_cast(freq) / 1000.0; // Convert MHz to GHz + frequency = + static_cast(freq) / 1000.0; // Convert MHz to GHz } } - spdlog::info( "FreeBSD CPU Frequency: {} GHz", frequency); + spdlog::info("FreeBSD CPU Frequency: {} GHz", frequency); return frequency; } auto getMinProcessorFrequency() -> double { - spdlog::info( "Starting getMinProcessorFrequency function on FreeBSD"); + spdlog::info("Starting getMinProcessorFrequency function on FreeBSD"); double minFreq = 0.0; @@ -304,7 +328,8 @@ auto getMinProcessorFrequency() -> double { // Some FreeBSD systems expose this information if (sysctlbyname("dev.cpu.0.freq_levels", NULL, &len, NULL, 0) != -1) { std::vector freqLevels(len); - if (sysctlbyname("dev.cpu.0.freq_levels", freqLevels.data(), &len, NULL, 0) != -1) { + if (sysctlbyname("dev.cpu.0.freq_levels", freqLevels.data(), &len, NULL, + 0) != -1) { std::string levels(freqLevels.begin(), freqLevels.end()); // Format is typically "frequency/power frequency/power ..." @@ -315,9 +340,11 @@ auto getMinProcessorFrequency() -> double { pos = lastLevel.find('/'); if (pos != std::string::npos) { try { - minFreq = std::stod(lastLevel.substr(0, pos)) / 1000.0; // Convert MHz to GHz + minFreq = std::stod(lastLevel.substr(0, pos)) / + 1000.0; // Convert MHz to GHz } catch (const std::exception& e) { - spdlog::warn( "Error parsing min frequency: {}", e.what()); + spdlog::warn("Error parsing min frequency: {}", + e.what()); } } } @@ -329,19 +356,20 @@ auto getMinProcessorFrequency() -> double { // As a fallback, estimate min as a fraction of current double currentFreq = getProcessorFrequency(); if (currentFreq > 0.0) { - minFreq = currentFreq * 0.5; // Estimate as half the current frequency - spdlog::info( "Estimating min CPU frequency as {} GHz", minFreq); + minFreq = + currentFreq * 0.5; // Estimate as half the current frequency + spdlog::info("Estimating min CPU frequency as {} GHz", minFreq); } else { - minFreq = 1.0; // Default fallback + minFreq = 1.0; // Default fallback } } - spdlog::info( "FreeBSD CPU Min Frequency: {} GHz", minFreq); + spdlog::info("FreeBSD CPU Min Frequency: {} GHz", minFreq); return minFreq; } auto getMaxProcessorFrequency() -> double { - spdlog::info( "Starting getMaxProcessorFrequency function on FreeBSD"); + spdlog::info("Starting getMaxProcessorFrequency function on FreeBSD"); double maxFreq = 0.0; @@ -352,7 +380,8 @@ auto getMaxProcessorFrequency() -> double { // Some FreeBSD systems expose this information if (sysctlbyname("dev.cpu.0.freq_levels", NULL, &len, NULL, 0) != -1) { std::vector freqLevels(len); - if (sysctlbyname("dev.cpu.0.freq_levels", freqLevels.data(), &len, NULL, 0) != -1) { + if (sysctlbyname("dev.cpu.0.freq_levels", freqLevels.data(), &len, NULL, + 0) != -1) { std::string levels(freqLevels.begin(), freqLevels.end()); // Format is typically "frequency/power frequency/power ..." @@ -360,9 +389,10 @@ auto getMaxProcessorFrequency() -> double { size_t pos = levels.find('/'); if (pos != std::string::npos) { try { - maxFreq = std::stod(levels.substr(0, pos)) / 1000.0; // Convert MHz to GHz + maxFreq = std::stod(levels.substr(0, pos)) / + 1000.0; // Convert MHz to GHz } catch (const std::exception& e) { - spdlog::warn( "Error parsing max frequency: {}", e.what()); + spdlog::warn("Error parsing max frequency: {}", e.what()); } } } @@ -371,15 +401,15 @@ auto getMaxProcessorFrequency() -> double { // If we couldn't find a max frequency, use current as fallback if (maxFreq <= 0.0) { maxFreq = getProcessorFrequency(); - spdlog::info( "Using current CPU frequency as max: {} GHz", maxFreq); + spdlog::info("Using current CPU frequency as max: {} GHz", maxFreq); } - spdlog::info( "FreeBSD CPU Max Frequency: {} GHz", maxFreq); + spdlog::info("FreeBSD CPU Max Frequency: {} GHz", maxFreq); return maxFreq; } auto getPerCoreFrequencies() -> std::vector { - spdlog::info( "Starting getPerCoreFrequencies function on FreeBSD"); + spdlog::info("Starting getPerCoreFrequencies function on FreeBSD"); int numCores = getNumberOfLogicalCores(); std::vector frequencies(numCores, 0.0); @@ -393,7 +423,8 @@ auto getPerCoreFrequencies() -> std::vector { if (sysctlbyname(sysctlName.c_str(), &freq, &len, NULL, 0) != -1) { // dev.cpu.N.freq returns frequency in MHz - frequencies[i] = static_cast(freq) / 1000.0; // Convert MHz to GHz + frequencies[i] = + static_cast(freq) / 1000.0; // Convert MHz to GHz } else { // Fall back to overall CPU frequency if (i == 0) { @@ -404,12 +435,13 @@ auto getPerCoreFrequencies() -> std::vector { } } - spdlog::info( "FreeBSD Per-Core CPU Frequencies collected for {} cores", numCores); + spdlog::info("FreeBSD Per-Core CPU Frequencies collected for {} cores", + numCores); return frequencies; } auto getNumberOfPhysicalPackages() -> int { - spdlog::info( "Starting getNumberOfPhysicalPackages function on FreeBSD"); + spdlog::info("Starting getNumberOfPhysicalPackages function on FreeBSD"); if (!needsCacheRefresh() && g_cpuInfoCache.numPhysicalPackages > 0) { return g_cpuInfoCache.numPhysicalPackages; @@ -423,16 +455,17 @@ auto getNumberOfPhysicalPackages() -> int { int packages = 0; size_t len = sizeof(packages); - if (sysctlbyname("hw.packages", &packages, &len, NULL, 0) != -1 && packages > 0) { + if (sysctlbyname("hw.packages", &packages, &len, NULL, 0) != -1 && + packages > 0) { numberOfPackages = packages; } - spdlog::info( "FreeBSD Physical CPU Packages: {}", numberOfPackages); + spdlog::info("FreeBSD Physical CPU Packages: {}", numberOfPackages); return numberOfPackages; } auto getNumberOfPhysicalCores() -> int { - spdlog::info( "Starting getNumberOfPhysicalCores function on FreeBSD"); + spdlog::info("Starting getNumberOfPhysicalCores function on FreeBSD"); if (!needsCacheRefresh() && g_cpuInfoCache.numPhysicalCores > 0) { return g_cpuInfoCache.numPhysicalCores; @@ -452,8 +485,11 @@ auto getNumberOfPhysicalCores() -> int { int hyperThreading = 0; len = sizeof(hyperThreading); - if (sysctlbyname("hw.cpu_hyperthreading", &hyperThreading, &len, NULL, 0) != -1 && hyperThreading) { - numberOfCores /= 2; // If hyperthreading is enabled, logical cores = 2 * physical cores + if (sysctlbyname("hw.cpu_hyperthreading", &hyperThreading, &len, NULL, + 0) != -1 && + hyperThreading) { + numberOfCores /= 2; // If hyperthreading is enabled, logical cores + // = 2 * physical cores } } @@ -462,12 +498,12 @@ auto getNumberOfPhysicalCores() -> int { numberOfCores = 1; } - spdlog::info( "FreeBSD Physical CPU Cores: {}", numberOfCores); + spdlog::info("FreeBSD Physical CPU Cores: {}", numberOfCores); return numberOfCores; } auto getNumberOfLogicalCores() -> int { - spdlog::info( "Starting getNumberOfLogicalCores function on FreeBSD"); + spdlog::info("Starting getNumberOfLogicalCores function on FreeBSD"); if (!needsCacheRefresh() && g_cpuInfoCache.numLogicalCores > 0) { return g_cpuInfoCache.numLogicalCores; @@ -491,12 +527,12 @@ auto getNumberOfLogicalCores() -> int { numberOfCores = 1; } - spdlog::info( "FreeBSD Logical CPU Cores: {}", numberOfCores); + spdlog::info("FreeBSD Logical CPU Cores: {}", numberOfCores); return numberOfCores; } auto getCacheSizes() -> CacheSizes { - spdlog::info( "Starting getCacheSizes function on FreeBSD"); + spdlog::info("Starting getCacheSizes function on FreeBSD"); if (!needsCacheRefresh() && (g_cpuInfoCache.caches.l1d > 0 || g_cpuInfoCache.caches.l2 > 0 || @@ -540,14 +576,15 @@ auto getCacheSizes() -> CacheSizes { cacheSizes.l3_line_size = lineSize; } - spdlog::info( "FreeBSD Cache Sizes: L1d={}KB, L1i={}KB, L2={}KB, L3={}KB", - cacheSizes.l1d / 1024, cacheSizes.l1i / 1024, cacheSizes.l2 / 1024, cacheSizes.l3 / 1024); + spdlog::info("FreeBSD Cache Sizes: L1d={}KB, L1i={}KB, L2={}KB, L3={}KB", + cacheSizes.l1d / 1024, cacheSizes.l1i / 1024, + cacheSizes.l2 / 1024, cacheSizes.l3 / 1024); return cacheSizes; } auto getCpuLoadAverage() -> LoadAverage { - spdlog::info( "Starting getCpuLoadAverage function on FreeBSD"); + spdlog::info("Starting getCpuLoadAverage function on FreeBSD"); LoadAverage loadAvg{0.0, 0.0, 0.0}; @@ -558,25 +595,25 @@ auto getCpuLoadAverage() -> LoadAverage { loadAvg.fifteenMinutes = avg[2]; } - spdlog::info( "FreeBSD Load Average: {}, {}, {}", - loadAvg.oneMinute, loadAvg.fiveMinutes, loadAvg.fifteenMinutes); + spdlog::info("FreeBSD Load Average: {}, {}, {}", loadAvg.oneMinute, + loadAvg.fiveMinutes, loadAvg.fifteenMinutes); return loadAvg; } auto getCpuPowerInfo() -> CpuPowerInfo { - spdlog::info( "Starting getCpuPowerInfo function on FreeBSD"); + spdlog::info("Starting getCpuPowerInfo function on FreeBSD"); CpuPowerInfo powerInfo{0.0, 0.0, 0.0}; // FreeBSD doesn't provide CPU power information through a simple API - spdlog::info( "FreeBSD CPU Power Info: Not implemented"); + spdlog::info("FreeBSD CPU Power Info: Not implemented"); return powerInfo; } auto getCpuFeatureFlags() -> std::vector { - spdlog::info( "Starting getCpuFeatureFlags function on FreeBSD"); + spdlog::info("Starting getCpuFeatureFlags function on FreeBSD"); if (!needsCacheRefresh() && !g_cpuInfoCache.flags.empty()) { return g_cpuInfoCache.flags; @@ -624,16 +661,17 @@ auto getCpuFeatureFlags() -> std::vector { std::sort(flags.begin(), flags.end()); flags.erase(std::unique(flags.begin(), flags.end()), flags.end()); - spdlog::info( "FreeBSD CPU Flags: {} features collected", flags.size()); + spdlog::info("FreeBSD CPU Flags: {} features collected", flags.size()); return flags; } auto getCpuArchitecture() -> CpuArchitecture { - spdlog::info( "Starting getCpuArchitecture function on FreeBSD"); + spdlog::info("Starting getCpuArchitecture function on FreeBSD"); if (!needsCacheRefresh()) { std::lock_guard lock(g_cacheMutex); - if (g_cacheInitialized && g_cpuInfoCache.architecture != CpuArchitecture::UNKNOWN) { + if (g_cacheInitialized && + g_cpuInfoCache.architecture != CpuArchitecture::UNKNOWN) { return g_cpuInfoCache.architecture; } } @@ -662,12 +700,12 @@ auto getCpuArchitecture() -> CpuArchitecture { } } - spdlog::info( "FreeBSD CPU Architecture: {}", cpuArchitectureToString(arch)); + spdlog::info("FreeBSD CPU Architecture: {}", cpuArchitectureToString(arch)); return arch; } auto getCpuVendor() -> CpuVendor { - spdlog::info( "Starting getCpuVendor function on FreeBSD"); + spdlog::info("Starting getCpuVendor function on FreeBSD"); if (!needsCacheRefresh()) { std::lock_guard lock(g_cacheMutex); @@ -688,7 +726,8 @@ auto getCpuVendor() -> CpuVendor { vendor = getVendorFromString(vendorString); - spdlog::info( "FreeBSD CPU Vendor: {} ({})", vendorString, cpuVendorToString(vendor)); + spdlog::info("FreeBSD CPU Vendor: {} ({})", vendorString, + cpuVendorToString(vendor)); return vendor; } @@ -707,15 +746,20 @@ auto getCpuSocketType() -> std::string { // Common socket type patterns based on CPU model if (model.find("Intel") != std::string::npos) { - if (model.find("Core i9") != std::string::npos || model.find("Core i7") != std::string::npos || - model.find("Core i5") != std::string::npos || model.find("Core i3") != std::string::npos) { + if (model.find("Core i9") != std::string::npos || + model.find("Core i7") != std::string::npos || + model.find("Core i5") != std::string::npos || + model.find("Core i3") != std::string::npos) { // Modern Intel desktop/laptop CPUs - if (model.find("12th Gen") != std::string::npos || model.find("13th Gen") != std::string::npos || + if (model.find("12th Gen") != std::string::npos || + model.find("13th Gen") != std::string::npos || model.find("14th Gen") != std::string::npos) { socketType = "LGA1700"; - } else if (model.find("10th Gen") != std::string::npos || model.find("11th Gen") != std::string::npos) { + } else if (model.find("10th Gen") != std::string::npos || + model.find("11th Gen") != std::string::npos) { socketType = "LGA1200"; - } else if (model.find("8th Gen") != std::string::npos || model.find("9th Gen") != std::string::npos) { + } else if (model.find("8th Gen") != std::string::npos || + model.find("9th Gen") != std::string::npos) { socketType = "LGA1151"; } else { socketType = "Intel Socket (Unknown Generation)"; @@ -727,7 +771,8 @@ auto getCpuSocketType() -> std::string { if (model.find("Ryzen") != std::string::npos) { if (model.find("7000") != std::string::npos) { socketType = "AM5"; - } else if (model.find("5000") != std::string::npos || model.find("3000") != std::string::npos) { + } else if (model.find("5000") != std::string::npos || + model.find("3000") != std::string::npos) { socketType = "AM4"; } else { socketType = "AMD Socket (Unknown Generation)"; @@ -739,7 +784,8 @@ auto getCpuSocketType() -> std::string { } } - spdlog::info("FreeBSD CPU Socket Type: {} (inferred from model)", socketType); + spdlog::info("FreeBSD CPU Socket Type: {} (inferred from model)", + socketType); std::lock_guard lock(g_cacheMutex); g_cpuInfoCache.socketType = socketType; @@ -748,7 +794,7 @@ auto getCpuSocketType() -> std::string { } auto getCpuScalingGovernor() -> std::string { - spdlog::info( "Starting getCpuScalingGovernor function on FreeBSD"); + spdlog::info("Starting getCpuScalingGovernor function on FreeBSD"); std::string governor = "Unknown"; @@ -769,7 +815,8 @@ auto getCpuScalingGovernor() -> std::string { int economy = 0, performance = 0; size_t len = sizeof(economy); - if (sysctlbyname("hw.acpi.cpu.px_dom0.select", &economy, &len, NULL, 0) != -1) { + if (sysctlbyname("hw.acpi.cpu.px_dom0.select", &economy, &len, NULL, + 0) != -1) { if (economy == 0) { governor = "performance"; } else { @@ -778,12 +825,12 @@ auto getCpuScalingGovernor() -> std::string { } } - spdlog::info( "FreeBSD CPU Scaling Governor: {}", governor); + spdlog::info("FreeBSD CPU Scaling Governor: {}", governor); return governor; } auto getPerCoreScalingGovernors() -> std::vector { - spdlog::info( "Starting getPerCoreScalingGovernors function on FreeBSD"); + spdlog::info("Starting getPerCoreScalingGovernors function on FreeBSD"); int numCores = getNumberOfLogicalCores(); std::vector governors(numCores); @@ -795,10 +842,11 @@ auto getPerCoreScalingGovernors() -> std::vector { governors[i] = governor; } - spdlog::info( "FreeBSD Per-Core Scaling Governors: {} (same for all cores)", governor); + spdlog::info("FreeBSD Per-Core Scaling Governors: {} (same for all cores)", + governor); return governors; } -} // namespace atom::system +} // namespace atom::system #endif /* __FreeBSD__ */ diff --git a/atom/sysinfo/hardware/cpu/windows.cpp b/atom/sysinfo/hardware/cpu/windows.cpp index 5818eedb..6fba70f4 100644 --- a/atom/sysinfo/hardware/cpu/windows.cpp +++ b/atom/sysinfo/hardware/cpu/windows.cpp @@ -15,8 +15,8 @@ Description: System Information Module - CPU Windows Implementation #include #ifdef _WIN32 -#include "common.hpp" #include +#include "common.hpp" #include #include // Add this header for PowerGetActiveScheme @@ -36,7 +36,7 @@ auto getCPUModel_Windows() -> std::string; // 这里应该添加所有函数的前向声明 auto getCurrentCpuUsage_Windows() -> float { - spdlog::info( "Starting getCurrentCpuUsage function on Windows"); + spdlog::info("Starting getCurrentCpuUsage function on Windows"); static PDH_HQUERY cpuQuery = nullptr; static PDH_HCOUNTER cpuTotal = nullptr; @@ -65,12 +65,12 @@ auto getCurrentCpuUsage_Windows() -> float { // Clamp the value between 0 and 100 cpuUsage = std::max(0.0F, std::min(100.0F, cpuUsage)); - spdlog::info( "Windows CPU Usage: {}%", cpuUsage); + spdlog::info("Windows CPU Usage: {}%", cpuUsage); return cpuUsage; } auto getPerCoreCpuUsage() -> std::vector { - spdlog::info( "Starting getPerCoreCpuUsage function on Windows"); + spdlog::info("Starting getPerCoreCpuUsage function on Windows"); static PDH_HQUERY cpuQuery = nullptr; static std::vector cpuCounters; @@ -109,7 +109,7 @@ auto getPerCoreCpuUsage() -> std::vector { coreUsages[i] = std::max(0.0F, std::min(100.0F, coreUsages[i])); } - spdlog::info( "Windows Per-Core CPU Usage collected for {} cores", numCores); + spdlog::info("Windows Per-Core CPU Usage collected for {} cores", numCores); return coreUsages; } @@ -141,26 +141,29 @@ auto getCurrentCpuTemperature() -> float { IID_IWbemLocator, (LPVOID *)&pLoc); if (FAILED(hres)) { - spdlog::error("Failed to create IWbemLocator object. Error code: {}", hres); + spdlog::error("Failed to create IWbemLocator object. Error code: {}", + hres); CoUninitialize(); return temperature; } // Connect to WMI through the IWbemLocator::ConnectServer method IWbemServices *pSvc = nullptr; - hres = pLoc->ConnectServer(_bstr_t(L"ROOT\\WMI"), nullptr, nullptr, 0, - 0, 0, 0, &pSvc); + hres = pLoc->ConnectServer(_bstr_t(L"ROOT\\WMI"), nullptr, nullptr, 0, 0, 0, + 0, &pSvc); if (FAILED(hres)) { - spdlog::warn("Could not connect to WMI namespace ROOT\\WMI. Error code: {}", hres); + spdlog::warn( + "Could not connect to WMI namespace ROOT\\WMI. Error code: {}", + hres); pLoc->Release(); CoUninitialize(); return temperature; } // Set security levels on the proxy - hres = CoSetProxyBlanket(pSvc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, - nullptr, RPC_C_AUTHN_LEVEL_CALL, + hres = CoSetProxyBlanket(pSvc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, nullptr, + RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, EOAC_NONE); if (FAILED(hres)) { @@ -174,8 +177,7 @@ auto getCurrentCpuTemperature() -> float { // Query for thermal zone temperature IEnumWbemClassObject *pEnumerator = nullptr; hres = pSvc->ExecQuery( - bstr_t("WQL"), - bstr_t("SELECT * FROM MSAcpi_ThermalZoneTemperature"), + bstr_t("WQL"), bstr_t("SELECT * FROM MSAcpi_ThermalZoneTemperature"), WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, nullptr, &pEnumerator); @@ -203,7 +205,9 @@ auto getCurrentCpuTemperature() -> float { if (SUCCEEDED(hr) && vtProp.vt == VT_I4) { // Temperature is in tenths of Kelvin, convert to Celsius temperature = (vtProp.lVal / 10.0f) - 273.15f; - spdlog::info("Windows CPU Temperature: {}°C (from thermal zone)", temperature); + spdlog::info( + "Windows CPU Temperature: {}°C (from thermal zone)", + temperature); } else { spdlog::warn("Failed to retrieve CurrentTemperature property"); } @@ -239,15 +243,20 @@ auto getPerCoreCpuTemperature() -> std::vector { float overallTemp = getCurrentCpuTemperature(); std::fill(temperatures.begin(), temperatures.end(), overallTemp); - spdlog::info("Windows Per-Core CPU Temperature: Using overall temperature ({} °C) for {} cores", - overallTemp, numCores); - spdlog::debug("Note: Windows does not provide native per-core temperature APIs. Consider using hardware monitoring libraries for detailed per-core data."); + spdlog::info( + "Windows Per-Core CPU Temperature: Using overall temperature ({} °C) " + "for {} cores", + overallTemp, numCores); + spdlog::debug( + "Note: Windows does not provide native per-core temperature APIs. " + "Consider using hardware monitoring libraries for detailed per-core " + "data."); return temperatures; } auto getCPUModel() -> std::string { - spdlog::info( "Starting getCPUModel function on Windows"); + spdlog::info("Starting getCPUModel function on Windows"); if (!needsCacheRefresh() && !g_cpuInfoCache.model.empty()) { return g_cpuInfoCache.model; @@ -275,12 +284,12 @@ auto getCPUModel() -> std::string { cpuModel.erase(0, cpuModel.find_first_not_of(" \t\n\r\f\v")); cpuModel.erase(cpuModel.find_last_not_of(" \t\n\r\f\v") + 1); - spdlog::info( "Windows CPU Model: {}", cpuModel); + spdlog::info("Windows CPU Model: {}", cpuModel); return cpuModel; } auto getProcessorIdentifier() -> std::string { - spdlog::info( "Starting getProcessorIdentifier function on Windows"); + spdlog::info("Starting getProcessorIdentifier function on Windows"); if (!needsCacheRefresh() && !g_cpuInfoCache.identifier.empty()) { return g_cpuInfoCache.identifier; @@ -318,12 +327,12 @@ auto getProcessorIdentifier() -> std::string { " Model " + std::to_string(model) + " Stepping " + std::to_string(stepping); - spdlog::info( "Windows CPU Identifier: {}", identifier); + spdlog::info("Windows CPU Identifier: {}", identifier); return identifier; } auto getProcessorFrequency() -> double { - spdlog::info( "Starting getProcessorFrequency function on Windows"); + spdlog::info("Starting getProcessorFrequency function on Windows"); DWORD bufSize = sizeof(DWORD); DWORD mhz = 0; @@ -334,16 +343,16 @@ auto getProcessorFrequency() -> double { "~MHz", RRF_RT_REG_DWORD, nullptr, &mhz, &bufSize) == ERROR_SUCCESS) { double frequency = static_cast(mhz) / 1000.0; - spdlog::info( "Windows CPU Frequency: {} GHz", frequency); + spdlog::info("Windows CPU Frequency: {} GHz", frequency); return frequency; } - spdlog::info( "Failed to get Windows CPU Frequency"); + spdlog::info("Failed to get Windows CPU Frequency"); return 0.0; } auto getMinProcessorFrequency() -> double { - spdlog::info( "Starting getMinProcessorFrequency function on Windows"); + spdlog::info("Starting getMinProcessorFrequency function on Windows"); // Windows doesn't provide a direct API for minimum CPU frequency // This would require reading from the registry or using WMI @@ -359,12 +368,12 @@ auto getMinProcessorFrequency() -> double { minFreq = currentFreq * 0.5; // Estimate as half the current frequency } - spdlog::info( "Windows CPU Min Frequency: {} GHz (estimated)", minFreq); + spdlog::info("Windows CPU Min Frequency: {} GHz (estimated)", minFreq); return minFreq; } auto getMaxProcessorFrequency() -> double { - spdlog::info( "Starting getMaxProcessorFrequency function on Windows"); + spdlog::info("Starting getMaxProcessorFrequency function on Windows"); DWORD bufSize = sizeof(DWORD); DWORD mhz = 0; @@ -375,16 +384,16 @@ auto getMaxProcessorFrequency() -> double { "~MHz", RRF_RT_REG_DWORD, nullptr, &mhz, &bufSize) == ERROR_SUCCESS) { double frequency = static_cast(mhz) / 1000.0; - spdlog::info( "Windows CPU Max Frequency: {} GHz", frequency); + spdlog::info("Windows CPU Max Frequency: {} GHz", frequency); return frequency; } - spdlog::info( "Failed to get Windows CPU Max Frequency"); + spdlog::info("Failed to get Windows CPU Max Frequency"); return getProcessorFrequency(); // Fallback to current frequency } auto getPerCoreFrequencies() -> std::vector { - spdlog::info( "Starting getPerCoreFrequencies function on Windows"); + spdlog::info("Starting getPerCoreFrequencies function on Windows"); int numCores = getNumberOfLogicalCores(); std::vector frequencies(numCores, 0.0); @@ -398,13 +407,13 @@ auto getPerCoreFrequencies() -> std::vector { frequencies[i] = frequency; } - spdlog::info( "Windows Per-Core CPU Frequencies: {} GHz (all cores)", - frequency); + spdlog::info("Windows Per-Core CPU Frequencies: {} GHz (all cores)", + frequency); return frequencies; } auto getNumberOfPhysicalPackages() -> int { - spdlog::info( "Starting getNumberOfPhysicalPackages function on Windows"); + spdlog::info("Starting getNumberOfPhysicalPackages function on Windows"); if (!needsCacheRefresh() && g_cpuInfoCache.numPhysicalPackages > 0) { return g_cpuInfoCache.numPhysicalPackages; @@ -418,12 +427,12 @@ auto getNumberOfPhysicalPackages() -> int { // Most desktop/laptop systems have 1 physical package numberOfPackages = 1; - spdlog::info( "Windows Physical CPU Packages: {}", numberOfPackages); + spdlog::info("Windows Physical CPU Packages: {}", numberOfPackages); return numberOfPackages; } auto getNumberOfPhysicalCores() -> int { - spdlog::info( "Starting getNumberOfPhysicalCores function on Windows"); + spdlog::info("Starting getNumberOfPhysicalCores function on Windows"); if (!needsCacheRefresh() && g_cpuInfoCache.numPhysicalCores > 0) { return g_cpuInfoCache.numPhysicalCores; @@ -453,12 +462,12 @@ auto getNumberOfPhysicalCores() -> int { // Ensure we have at least 1 core numberOfCores = std::max(1, numberOfCores); - spdlog::info( "Windows Physical CPU Cores: {}", numberOfCores); + spdlog::info("Windows Physical CPU Cores: {}", numberOfCores); return numberOfCores; } auto getNumberOfLogicalCores() -> int { - spdlog::info( "Starting getNumberOfLogicalCores function on Windows"); + spdlog::info("Starting getNumberOfLogicalCores function on Windows"); if (!needsCacheRefresh() && g_cpuInfoCache.numLogicalCores > 0) { return g_cpuInfoCache.numLogicalCores; @@ -469,12 +478,12 @@ auto getNumberOfLogicalCores() -> int { int numberOfCores = sysInfo.dwNumberOfProcessors; - spdlog::info( "Windows Logical CPU Cores: {}", numberOfCores); + spdlog::info("Windows Logical CPU Cores: {}", numberOfCores); return numberOfCores; } auto getCacheSizes() -> CacheSizes { - spdlog::info( "Starting getCacheSizes function on Windows"); + spdlog::info("Starting getCacheSizes function on Windows"); if (!needsCacheRefresh() && (g_cpuInfoCache.caches.l1d > 0 || g_cpuInfoCache.caches.l2 > 0 || @@ -533,15 +542,15 @@ auto getCacheSizes() -> CacheSizes { } } - spdlog::info( "Windows Cache Sizes: L1d={}KB, L1i={}KB, L2={}KB, L3={}KB", - cacheSizes.l1d / 1024, cacheSizes.l1i / 1024, cacheSizes.l2 / 1024, - cacheSizes.l3 / 1024); + spdlog::info("Windows Cache Sizes: L1d={}KB, L1i={}KB, L2={}KB, L3={}KB", + cacheSizes.l1d / 1024, cacheSizes.l1i / 1024, + cacheSizes.l2 / 1024, cacheSizes.l3 / 1024); return cacheSizes; } auto getCpuLoadAverage() -> LoadAverage { - spdlog::info( "Starting getCpuLoadAverage function on Windows"); + spdlog::info("Starting getCpuLoadAverage function on Windows"); LoadAverage loadAvg{0.0, 0.0, 0.0}; @@ -559,14 +568,14 @@ auto getCpuLoadAverage() -> LoadAverage { loadAvg.fifteenMinutes = load; spdlog::info( - "Windows Load Average (approximated from CPU usage): {}, {}, {}", - loadAvg.oneMinute, loadAvg.fiveMinutes, loadAvg.fifteenMinutes); + "Windows Load Average (approximated from CPU usage): {}, {}, {}", + loadAvg.oneMinute, loadAvg.fiveMinutes, loadAvg.fifteenMinutes); return loadAvg; } auto getCpuPowerInfo() -> CpuPowerInfo { - spdlog::info( "Starting getCpuPowerInfo function on Windows"); + spdlog::info("Starting getCpuPowerInfo function on Windows"); CpuPowerInfo powerInfo{0.0, 0.0, 0.0}; @@ -578,15 +587,15 @@ auto getCpuPowerInfo() -> CpuPowerInfo { // based on the processor model spdlog::info( - "Windows CPU Power Info: currentWatts={}, maxTDP={}, energyImpact={} " - "(placeholder values)", - powerInfo.currentWatts, powerInfo.maxTDP, powerInfo.energyImpact); + "Windows CPU Power Info: currentWatts={}, maxTDP={}, energyImpact={} " + "(placeholder values)", + powerInfo.currentWatts, powerInfo.maxTDP, powerInfo.energyImpact); return powerInfo; } auto getCpuFeatureFlags() -> std::vector { - spdlog::info( "Starting getCpuFeatureFlags function on Windows"); + spdlog::info("Starting getCpuFeatureFlags function on Windows"); if (!needsCacheRefresh() && !g_cpuInfoCache.flags.empty()) { return g_cpuInfoCache.flags; @@ -767,13 +776,13 @@ auto getCpuFeatureFlags() -> std::vector { if (cpuInfo[2] & (1 << 6)) flags.push_back("avx512vbmi2"); - spdlog::info( "Windows CPU Flags: {} features collected", flags.size()); + spdlog::info("Windows CPU Flags: {} features collected", flags.size()); return flags; } auto getCpuArchitecture() -> CpuArchitecture { - spdlog::info( "Starting getCpuArchitecture function on Windows"); + spdlog::info("Starting getCpuArchitecture function on Windows"); if (!needsCacheRefresh()) { std::lock_guard lock(g_cacheMutex); @@ -806,12 +815,12 @@ auto getCpuArchitecture() -> CpuArchitecture { break; } - spdlog::info( "Windows CPU Architecture: {}", cpuArchitectureToString(arch)); + spdlog::info("Windows CPU Architecture: {}", cpuArchitectureToString(arch)); return arch; } auto getCpuVendor() -> CpuVendor { - spdlog::info( "Starting getCpuVendor function on Windows"); + spdlog::info("Starting getCpuVendor function on Windows"); if (!needsCacheRefresh()) { std::lock_guard lock(g_cacheMutex); @@ -835,8 +844,8 @@ auto getCpuVendor() -> CpuVendor { vendorString = vendorID; vendor = getVendorFromString(vendorString); - spdlog::info( "Windows CPU Vendor: {} ({})", vendorString, - cpuVendorToString(vendor)); + spdlog::info("Windows CPU Vendor: {} ({})", vendorString, + cpuVendorToString(vendor)); return vendor; } @@ -867,8 +876,8 @@ auto getCpuSocketType() -> std::string { if (SUCCEEDED(hres)) { CoSetProxyBlanket(pSvc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, - nullptr, RPC_C_AUTHN_LEVEL_CALL, - RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, EOAC_NONE); + nullptr, RPC_C_AUTHN_LEVEL_CALL, + RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, EOAC_NONE); IEnumWbemClassObject *pEnumerator = nullptr; hres = pSvc->ExecQuery( @@ -881,16 +890,23 @@ auto getCpuSocketType() -> std::string { IWbemClassObject *pclsObj = nullptr; ULONG uReturn = 0; - if (pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn) == S_OK && uReturn != 0) { + if (pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn) == + S_OK && + uReturn != 0) { VARIANT vtProp; VariantInit(&vtProp); - if (pclsObj->Get(L"SocketDesignation", 0, &vtProp, 0, 0) == S_OK) { + if (pclsObj->Get(L"SocketDesignation", 0, &vtProp, 0, 0) == + S_OK) { if (vtProp.vt == VT_BSTR && vtProp.bstrVal != nullptr) { - int size = WideCharToMultiByte(CP_UTF8, 0, vtProp.bstrVal, -1, nullptr, 0, nullptr, nullptr); + int size = WideCharToMultiByte( + CP_UTF8, 0, vtProp.bstrVal, -1, nullptr, 0, + nullptr, nullptr); if (size > 0) { std::vector buffer(size); - WideCharToMultiByte(CP_UTF8, 0, vtProp.bstrVal, -1, buffer.data(), size, nullptr, nullptr); + WideCharToMultiByte(CP_UTF8, 0, vtProp.bstrVal, + -1, buffer.data(), size, + nullptr, nullptr); socketType = buffer.data(); } } @@ -916,14 +932,19 @@ auto getCpuSocketType() -> std::string { std::string model = getCPUModel(); if (model.find("Intel") != std::string::npos) { - if (model.find("Core i9") != std::string::npos || model.find("Core i7") != std::string::npos || - model.find("Core i5") != std::string::npos || model.find("Core i3") != std::string::npos) { - if (model.find("12th Gen") != std::string::npos || model.find("13th Gen") != std::string::npos || + if (model.find("Core i9") != std::string::npos || + model.find("Core i7") != std::string::npos || + model.find("Core i5") != std::string::npos || + model.find("Core i3") != std::string::npos) { + if (model.find("12th Gen") != std::string::npos || + model.find("13th Gen") != std::string::npos || model.find("14th Gen") != std::string::npos) { socketType = "LGA1700"; - } else if (model.find("10th Gen") != std::string::npos || model.find("11th Gen") != std::string::npos) { + } else if (model.find("10th Gen") != std::string::npos || + model.find("11th Gen") != std::string::npos) { socketType = "LGA1200"; - } else if (model.find("8th Gen") != std::string::npos || model.find("9th Gen") != std::string::npos) { + } else if (model.find("8th Gen") != std::string::npos || + model.find("9th Gen") != std::string::npos) { socketType = "LGA1151"; } } else if (model.find("Xeon") != std::string::npos) { @@ -933,7 +954,8 @@ auto getCpuSocketType() -> std::string { if (model.find("Ryzen") != std::string::npos) { if (model.find("7000") != std::string::npos) { socketType = "AM5"; - } else if (model.find("5000") != std::string::npos || model.find("3000") != std::string::npos) { + } else if (model.find("5000") != std::string::npos || + model.find("3000") != std::string::npos) { socketType = "AM4"; } } else if (model.find("EPYC") != std::string::npos) { @@ -953,11 +975,11 @@ auto getCpuSocketType() -> std::string { } auto getCpuScalingGovernor() -> std::string { - spdlog::info( "Starting getCpuScalingGovernor function on Windows"); + spdlog::info("Starting getCpuScalingGovernor function on Windows"); std::string governor = "Unknown"; - GUID* activePlanGuid = NULL; + GUID *activePlanGuid = NULL; if (PowerGetActiveScheme(NULL, &activePlanGuid) == ERROR_SUCCESS) { // First, get the required buffer size DWORD bufferSize = 0; @@ -992,12 +1014,12 @@ auto getCpuScalingGovernor() -> std::string { LocalFree(activePlanGuid); } - spdlog::info( "Windows Power Plan: {}", governor); + spdlog::info("Windows Power Plan: {}", governor); return governor; } auto getPerCoreScalingGovernors() -> std::vector { - spdlog::info( "Starting getPerCoreScalingGovernors function on Windows"); + spdlog::info("Starting getPerCoreScalingGovernors function on Windows"); int numCores = getNumberOfLogicalCores(); std::vector governors(numCores); @@ -1010,15 +1032,13 @@ auto getPerCoreScalingGovernors() -> std::vector { governors[i] = governor; } - spdlog::info( "Windows Per-Core Power Plans: {} (same for all cores)", - governor); + spdlog::info("Windows Per-Core Power Plans: {} (same for all cores)", + governor); return governors; } // Wrapper function for getCurrentCpuUsage (the only one missing) -auto getCurrentCpuUsage() -> float { - return getCurrentCpuUsage_Windows(); -} +auto getCurrentCpuUsage() -> float { return getCurrentCpuUsage_Windows(); } } // namespace atom::system diff --git a/atom/sysinfo/hardware/memory/common.cpp b/atom/sysinfo/hardware/memory/common.cpp index c6b5f175..a4d65efa 100644 --- a/atom/sysinfo/hardware/memory/common.cpp +++ b/atom/sysinfo/hardware/memory/common.cpp @@ -18,7 +18,6 @@ #include #include "memory.hpp" - #ifdef _WIN32 #include #include @@ -179,7 +178,8 @@ auto getMemoryFragmentation() -> double { size_t allocatableSize = 0; try { constexpr size_t MAX_ALLOC_SIZE = 100 * 1024 * 1024; // 100 MB - const size_t testSize = std::min(MAX_ALLOC_SIZE, static_cast(available)); + const size_t testSize = + std::min(MAX_ALLOC_SIZE, static_cast(available)); std::vector testAlloc; testAlloc.reserve(testSize); allocatableSize = testAlloc.capacity(); diff --git a/atom/sysinfo/hardware/memory/linux.cpp b/atom/sysinfo/hardware/memory/linux.cpp index b53d848e..747c58e3 100644 --- a/atom/sysinfo/hardware/memory/linux.cpp +++ b/atom/sysinfo/hardware/memory/linux.cpp @@ -186,7 +186,7 @@ auto getPhysicalMemoryInfo() -> MemoryInfo::MemorySlot { auto getVirtualMemoryMax() -> unsigned long long { spdlog::debug("Getting virtual memory max (Linux)"); - struct sysinfo si{}; + struct sysinfo si {}; if (sysinfo(&si) != 0) { spdlog::error("Failed to get system info"); return 0ULL; @@ -200,7 +200,7 @@ auto getVirtualMemoryMax() -> unsigned long long { auto getVirtualMemoryUsed() -> unsigned long long { spdlog::debug("Getting virtual memory used (Linux)"); - struct sysinfo si{}; + struct sysinfo si {}; if (sysinfo(&si) != 0) { spdlog::error("Failed to get system info"); return 0ULL; @@ -215,7 +215,7 @@ auto getVirtualMemoryUsed() -> unsigned long long { auto getSwapMemoryTotal() -> unsigned long long { spdlog::debug("Getting swap memory total (Linux)"); - struct sysinfo si{}; + struct sysinfo si {}; if (sysinfo(&si) != 0) { spdlog::error("Failed to get system info"); return 0ULL; @@ -229,7 +229,7 @@ auto getSwapMemoryTotal() -> unsigned long long { auto getSwapMemoryUsed() -> unsigned long long { spdlog::debug("Getting swap memory used (Linux)"); - struct sysinfo si{}; + struct sysinfo si {}; if (sysinfo(&si) != 0) { spdlog::error("Failed to get system info"); return 0ULL; @@ -271,7 +271,7 @@ auto getDetailedMemoryStats() -> MemoryInfo { spdlog::debug("Getting detailed memory stats (Linux)"); MemoryInfo info; - struct sysinfo si{}; + struct sysinfo si {}; if (sysinfo(&si) == 0) { info.totalPhysicalMemory = si.totalram; @@ -481,7 +481,8 @@ auto getMemoryLoadPercentage() -> float { const auto available = availableIt->second; const auto used = total - available; const auto load = static_cast(used) / total * 100.0f; - spdlog::debug("Memory load percentage: {:.2f}% (used: {} kB, total: {} kB)", load, used, total); + spdlog::debug("Memory load percentage: {:.2f}% (used: {} kB, total: {} kB)", + load, used, total); return load; } diff --git a/atom/sysinfo/hardware/memory/memory.cpp b/atom/sysinfo/hardware/memory/memory.cpp index 5e92c9e3..d5a99c3d 100644 --- a/atom/sysinfo/hardware/memory/memory.cpp +++ b/atom/sysinfo/hardware/memory/memory.cpp @@ -35,7 +35,7 @@ auto getMemoryUsage() -> float { #elif defined(__APPLE__) return macos::getMemoryUsage(); #else - spdlog::error( "getMemoryUsage: Unsupported platform"); + spdlog::error("getMemoryUsage: Unsupported platform"); return 0.0f; #endif } @@ -48,7 +48,7 @@ auto getTotalMemorySize() -> unsigned long long { #elif defined(__APPLE__) return macos::getTotalMemorySize(); #else - spdlog::error( "getTotalMemorySize: Unsupported platform"); + spdlog::error("getTotalMemorySize: Unsupported platform"); return 0; #endif } @@ -61,7 +61,7 @@ auto getAvailableMemorySize() -> unsigned long long { #elif defined(__APPLE__) return macos::getAvailableMemorySize(); #else - spdlog::error( "getAvailableMemorySize: Unsupported platform"); + spdlog::error("getAvailableMemorySize: Unsupported platform"); return 0; #endif } @@ -74,7 +74,7 @@ auto getPhysicalMemoryInfo() -> MemoryInfo::MemorySlot { #elif defined(__APPLE__) return macos::getPhysicalMemoryInfo(); #else - spdlog::error( "getPhysicalMemoryInfo: Unsupported platform"); + spdlog::error("getPhysicalMemoryInfo: Unsupported platform"); return MemoryInfo::MemorySlot(); #endif } @@ -87,7 +87,7 @@ auto getVirtualMemoryMax() -> unsigned long long { #elif defined(__APPLE__) return macos::getVirtualMemoryMax(); #else - spdlog::error( "getVirtualMemoryMax: Unsupported platform"); + spdlog::error("getVirtualMemoryMax: Unsupported platform"); return 0; #endif } @@ -100,7 +100,7 @@ auto getVirtualMemoryUsed() -> unsigned long long { #elif defined(__APPLE__) return macos::getVirtualMemoryUsed(); #else - spdlog::error( "getVirtualMemoryUsed: Unsupported platform"); + spdlog::error("getVirtualMemoryUsed: Unsupported platform"); return 0; #endif } @@ -113,7 +113,7 @@ auto getSwapMemoryTotal() -> unsigned long long { #elif defined(__APPLE__) return macos::getSwapMemoryTotal(); #else - spdlog::error( "getSwapMemoryTotal: Unsupported platform"); + spdlog::error("getSwapMemoryTotal: Unsupported platform"); return 0; #endif } @@ -126,7 +126,7 @@ auto getSwapMemoryUsed() -> unsigned long long { #elif defined(__APPLE__) return macos::getSwapMemoryUsed(); #else - spdlog::error( "getSwapMemoryUsed: Unsupported platform"); + spdlog::error("getSwapMemoryUsed: Unsupported platform"); return 0; #endif } @@ -139,7 +139,7 @@ auto getCommittedMemory() -> size_t { #elif defined(__APPLE__) return macos::getCommittedMemory(); #else - spdlog::error( "getCommittedMemory: Unsupported platform"); + spdlog::error("getCommittedMemory: Unsupported platform"); return 0; #endif } @@ -152,7 +152,7 @@ auto getUncommittedMemory() -> size_t { #elif defined(__APPLE__) return macos::getUncommittedMemory(); #else - spdlog::error( "getUncommittedMemory: Unsupported platform"); + spdlog::error("getUncommittedMemory: Unsupported platform"); return 0; #endif } @@ -165,7 +165,7 @@ auto getDetailedMemoryStats() -> MemoryInfo { #elif defined(__APPLE__) return macos::getDetailedMemoryStats(); #else - spdlog::error( "getDetailedMemoryStats: Unsupported platform"); + spdlog::error("getDetailedMemoryStats: Unsupported platform"); return MemoryInfo(); #endif } @@ -178,7 +178,7 @@ auto getPeakWorkingSetSize() -> size_t { #elif defined(__APPLE__) return macos::getPeakWorkingSetSize(); #else - spdlog::error( "getPeakWorkingSetSize: Unsupported platform"); + spdlog::error("getPeakWorkingSetSize: Unsupported platform"); return 0; #endif } @@ -191,7 +191,7 @@ auto getCurrentWorkingSetSize() -> size_t { #elif defined(__APPLE__) return macos::getCurrentWorkingSetSize(); #else - spdlog::error( "getCurrentWorkingSetSize: Unsupported platform"); + spdlog::error("getCurrentWorkingSetSize: Unsupported platform"); return 0; #endif } @@ -204,7 +204,7 @@ auto getPageFaultCount() -> size_t { #elif defined(__APPLE__) return macos::getPageFaultCount(); #else - spdlog::error( "getPageFaultCount: Unsupported platform"); + spdlog::error("getPageFaultCount: Unsupported platform"); return 0; #endif } @@ -217,7 +217,7 @@ auto getMemoryLoadPercentage() -> double { #elif defined(__APPLE__) return macos::getMemoryLoadPercentage(); #else - spdlog::error( "getMemoryLoadPercentage: Unsupported platform"); + spdlog::error("getMemoryLoadPercentage: Unsupported platform"); return 0.0; #endif } @@ -230,7 +230,7 @@ auto getMemoryPerformance() -> MemoryPerformance { #elif defined(__APPLE__) return macos::getMemoryPerformance(); #else - spdlog::error( "getMemoryPerformance: Unsupported platform"); + spdlog::error("getMemoryPerformance: Unsupported platform"); return MemoryPerformance(); #endif } @@ -244,4 +244,4 @@ auto getMemoryPerformance() -> MemoryPerformance { // - optimizeMemoryUsage // - analyzeMemoryBottlenecks -} // namespace atom::system +} // namespace atom::system diff --git a/atom/sysinfo/hardware/memory/windows.cpp b/atom/sysinfo/hardware/memory/windows.cpp index 3f7345bd..acd4f500 100644 --- a/atom/sysinfo/hardware/memory/windows.cpp +++ b/atom/sysinfo/hardware/memory/windows.cpp @@ -51,7 +51,7 @@ static PROCESS_MEMORY_COUNTERS getProcessMemoryCounters() { return pmc; } -} +} // namespace auto getMemoryUsage() -> float { spdlog::debug("Getting memory usage percentage"); @@ -61,12 +61,16 @@ auto getMemoryUsage() -> float { return 0.0f; } - const auto totalMemoryMB = static_cast(status.ullTotalPhys) / MB_DIVISOR; - const auto availableMemoryMB = static_cast(status.ullAvailPhys) / MB_DIVISOR; - const auto usagePercent = (totalMemoryMB - availableMemoryMB) / totalMemoryMB * 100.0f; + const auto totalMemoryMB = + static_cast(status.ullTotalPhys) / MB_DIVISOR; + const auto availableMemoryMB = + static_cast(status.ullAvailPhys) / MB_DIVISOR; + const auto usagePercent = + (totalMemoryMB - availableMemoryMB) / totalMemoryMB * 100.0f; - spdlog::debug("Memory usage: {:.2f}% (Total: {:.2f} MB, Available: {:.2f} MB)", - usagePercent, totalMemoryMB, availableMemoryMB); + spdlog::debug( + "Memory usage: {:.2f}% (Total: {:.2f} MB, Available: {:.2f} MB)", + usagePercent, totalMemoryMB, availableMemoryMB); return usagePercent; } @@ -114,7 +118,8 @@ auto getVirtualMemoryMax() -> unsigned long long { const auto status = getMemoryStatus(); if (status.ullTotalVirtual > 0) { - spdlog::debug("Maximum virtual memory: {} bytes", status.ullTotalVirtual); + spdlog::debug("Maximum virtual memory: {} bytes", + status.ullTotalVirtual); } return status.ullTotalVirtual; @@ -124,8 +129,10 @@ auto getVirtualMemoryUsed() -> unsigned long long { spdlog::debug("Getting used virtual memory size"); const auto status = getMemoryStatus(); - if (status.ullTotalVirtual > 0 && status.ullAvailVirtual <= status.ullTotalVirtual) { - const auto usedVirtual = status.ullTotalVirtual - status.ullAvailVirtual; + if (status.ullTotalVirtual > 0 && + status.ullAvailVirtual <= status.ullTotalVirtual) { + const auto usedVirtual = + status.ullTotalVirtual - status.ullAvailVirtual; spdlog::debug("Used virtual memory: {} bytes", usedVirtual); return usedVirtual; } @@ -148,7 +155,8 @@ auto getSwapMemoryUsed() -> unsigned long long { spdlog::debug("Getting used swap memory size"); const auto status = getMemoryStatus(); - if (status.ullTotalPageFile > 0 && status.ullAvailPageFile <= status.ullTotalPageFile) { + if (status.ullTotalPageFile > 0 && + status.ullAvailPageFile <= status.ullTotalPageFile) { const auto usedSwap = status.ullTotalPageFile - status.ullAvailPageFile; spdlog::debug("Used swap memory: {} bytes", usedSwap); return usedSwap; @@ -193,9 +201,11 @@ auto getDetailedMemoryStats() -> MemoryInfo { info.totalPhysicalMemory = memStatus.ullTotalPhys; info.availablePhysicalMemory = memStatus.ullAvailPhys; info.virtualMemoryMax = memStatus.ullTotalVirtual; - info.virtualMemoryUsed = memStatus.ullTotalVirtual - memStatus.ullAvailVirtual; + info.virtualMemoryUsed = + memStatus.ullTotalVirtual - memStatus.ullAvailVirtual; info.swapMemoryTotal = memStatus.ullTotalPageFile; - info.swapMemoryUsed = memStatus.ullTotalPageFile - memStatus.ullAvailPageFile; + info.swapMemoryUsed = + memStatus.ullTotalPageFile - memStatus.ullAvailPageFile; const auto pmc = getProcessMemoryCounters(); if (pmc.WorkingSetSize > 0) { @@ -208,7 +218,8 @@ auto getDetailedMemoryStats() -> MemoryInfo { } MemoryInfo::MemorySlot slot; - slot.capacity = std::to_string(info.totalPhysicalMemory / (1024 * 1024)); + slot.capacity = + std::to_string(info.totalPhysicalMemory / (1024 * 1024)); slot.type = "DDR"; slot.clockSpeed = "Unknown"; info.slots.push_back(std::move(slot)); @@ -224,7 +235,8 @@ auto getPeakWorkingSetSize() -> size_t { const auto pmc = getProcessMemoryCounters(); if (pmc.PeakWorkingSetSize > 0) { - spdlog::debug("Peak working set size: {} bytes", pmc.PeakWorkingSetSize); + spdlog::debug("Peak working set size: {} bytes", + pmc.PeakWorkingSetSize); } return pmc.PeakWorkingSetSize; @@ -273,10 +285,13 @@ auto getMemoryPerformance() -> MemoryPerformance { PDH_HCOUNTER writeCounter = nullptr; if (PdhOpenQuery(nullptr, 0, &query) == ERROR_SUCCESS) { - const auto addCounterResult1 = PdhAddCounterW(query, L"\\Memory\\Pages/sec", 0, &readCounter); - const auto addCounterResult2 = PdhAddCounterW(query, L"\\Memory\\Page Writes/sec", 0, &writeCounter); + const auto addCounterResult1 = + PdhAddCounterW(query, L"\\Memory\\Pages/sec", 0, &readCounter); + const auto addCounterResult2 = PdhAddCounterW( + query, L"\\Memory\\Page Writes/sec", 0, &writeCounter); - if (addCounterResult1 == ERROR_SUCCESS && addCounterResult2 == ERROR_SUCCESS) { + if (addCounterResult1 == ERROR_SUCCESS && + addCounterResult2 == ERROR_SUCCESS) { PdhCollectQueryData(query); std::this_thread::sleep_for(std::chrono::seconds(1)); PdhCollectQueryData(query); @@ -284,12 +299,17 @@ auto getMemoryPerformance() -> MemoryPerformance { PDH_FMT_COUNTERVALUE readValue{}; PDH_FMT_COUNTERVALUE writeValue{}; - const auto getValueResult1 = PdhGetFormattedCounterValue(readCounter, PDH_FMT_DOUBLE, nullptr, &readValue); - const auto getValueResult2 = PdhGetFormattedCounterValue(writeCounter, PDH_FMT_DOUBLE, nullptr, &writeValue); - - if (getValueResult1 == ERROR_SUCCESS && getValueResult2 == ERROR_SUCCESS) { - perf.readSpeed = readValue.doubleValue * PAGE_SIZE_KB * KB_TO_MB; - perf.writeSpeed = writeValue.doubleValue * PAGE_SIZE_KB * KB_TO_MB; + const auto getValueResult1 = PdhGetFormattedCounterValue( + readCounter, PDH_FMT_DOUBLE, nullptr, &readValue); + const auto getValueResult2 = PdhGetFormattedCounterValue( + writeCounter, PDH_FMT_DOUBLE, nullptr, &writeValue); + + if (getValueResult1 == ERROR_SUCCESS && + getValueResult2 == ERROR_SUCCESS) { + perf.readSpeed = + readValue.doubleValue * PAGE_SIZE_KB * KB_TO_MB; + perf.writeSpeed = + writeValue.doubleValue * PAGE_SIZE_KB * KB_TO_MB; } else { spdlog::warn("Failed to get formatted counter values"); } @@ -301,8 +321,12 @@ auto getMemoryPerformance() -> MemoryPerformance { spdlog::warn("Failed to open PDH query for memory performance"); } - const auto totalMemoryMB = static_cast(getTotalMemorySize()) / MB_DIVISOR; - perf.bandwidthUsage = totalMemoryMB > 0 ? (perf.readSpeed + perf.writeSpeed) / totalMemoryMB * 100.0 : 0.0; + const auto totalMemoryMB = + static_cast(getTotalMemorySize()) / MB_DIVISOR; + perf.bandwidthUsage = + totalMemoryMB > 0 + ? (perf.readSpeed + perf.writeSpeed) / totalMemoryMB * 100.0 + : 0.0; std::vector testData; testData.reserve(MEMORY_TEST_SIZE); @@ -313,10 +337,15 @@ auto getMemoryPerformance() -> MemoryPerformance { } const auto end = std::chrono::high_resolution_clock::now(); - perf.latency = std::chrono::duration_cast(end - start).count() / static_cast(MEMORY_TEST_SIZE); + perf.latency = + std::chrono::duration_cast(end - start) + .count() / + static_cast(MEMORY_TEST_SIZE); - spdlog::debug("Memory performance - Read: {:.2f} MB/s, Write: {:.2f} MB/s, Bandwidth: {:.1f}%, Latency: {:.2f} ns", - perf.readSpeed, perf.writeSpeed, perf.bandwidthUsage, perf.latency); + spdlog::debug( + "Memory performance - Read: {:.2f} MB/s, Write: {:.2f} MB/s, " + "Bandwidth: {:.1f}%, Latency: {:.2f} ns", + perf.readSpeed, perf.writeSpeed, perf.bandwidthUsage, perf.latency); return perf; } diff --git a/atom/sysinfo/info/sn.cpp b/atom/sysinfo/info/sn.cpp index 573225a7..042241c0 100644 --- a/atom/sysinfo/info/sn.cpp +++ b/atom/sysinfo/info/sn.cpp @@ -10,13 +10,17 @@ // Helper function to convert BSTR to std::string (MinGW compatible) static std::string BSTRToString(BSTR bstr) { - if (!bstr) return ""; + if (!bstr) + return ""; - int len = WideCharToMultiByte(CP_UTF8, 0, bstr, -1, nullptr, 0, nullptr, nullptr); - if (len <= 0) return ""; + int len = + WideCharToMultiByte(CP_UTF8, 0, bstr, -1, nullptr, 0, nullptr, nullptr); + if (len <= 0) + return ""; std::string result(len - 1, '\0'); - WideCharToMultiByte(CP_UTF8, 0, bstr, -1, &result[0], len, nullptr, nullptr); + WideCharToMultiByte(CP_UTF8, 0, bstr, -1, &result[0], len, nullptr, + nullptr); return result; } @@ -24,7 +28,10 @@ static std::string BSTRToString(BSTR bstr) { class BSTRWrapper { public: explicit BSTRWrapper(const wchar_t* str) : bstr_(SysAllocString(str)) {} - ~BSTRWrapper() { if (bstr_) SysFreeString(bstr_); } + ~BSTRWrapper() { + if (bstr_) + SysFreeString(bstr_); + } BSTRWrapper(const BSTRWrapper&) = delete; BSTRWrapper& operator=(const BSTRWrapper&) = delete; @@ -65,9 +72,8 @@ class HardwareInfo::Impl { BSTRWrapper wql(L"WQL"); BSTRWrapper query((L"SELECT * FROM " + wmiClass).c_str()); HRESULT hres = pSvc->ExecQuery( - wql, query, - WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, nullptr, - &pEnumerator); + wql, query, WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, + nullptr, &pEnumerator); if (FAILED(hres)) { spdlog::error("WMI query execution failed with HRESULT: 0x{:x}", @@ -77,8 +83,8 @@ class HardwareInfo::Impl { } while (pEnumerator) { - HRESULT hr = - pEnumerator->Next(static_cast(WBEM_INFINITE), 1, &pclsObj, &uReturn); + HRESULT hr = pEnumerator->Next(static_cast(WBEM_INFINITE), 1, + &pclsObj, &uReturn); if (0 == uReturn) { break; } @@ -127,9 +133,8 @@ class HardwareInfo::Impl { BSTRWrapper wql2(L"WQL"); BSTRWrapper query2((L"SELECT * FROM " + wmiClass).c_str()); HRESULT hres = pSvc->ExecQuery( - wql2, query2, - WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, nullptr, - &pEnumerator); + wql2, query2, WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, + nullptr, &pEnumerator); if (FAILED(hres)) { spdlog::error("WMI query execution failed with HRESULT: 0x{:x}", @@ -139,8 +144,8 @@ class HardwareInfo::Impl { } while (pEnumerator) { - HRESULT hr = - pEnumerator->Next(static_cast(WBEM_INFINITE), 1, &pclsObj, &uReturn); + HRESULT hr = pEnumerator->Next(static_cast(WBEM_INFINITE), 1, + &pclsObj, &uReturn); if (0 == uReturn) { break; } @@ -167,8 +172,8 @@ class HardwareInfo::Impl { * @param pSvc WMI services pointer * @return true if initialization successful, false otherwise */ - static auto initializeWmi(IWbemLocator*& pLoc, IWbemServices*& pSvc) - -> bool { + static auto initializeWmi(IWbemLocator*& pLoc, + IWbemServices*& pSvc) -> bool { spdlog::debug("Initializing WMI components"); HRESULT hres = CoInitializeEx(nullptr, COINIT_MULTITHREADED); @@ -201,8 +206,8 @@ class HardwareInfo::Impl { } BSTRWrapper rootCimv2(L"ROOT\\CIMV2"); - hres = pLoc->ConnectServer(rootCimv2, nullptr, nullptr, 0, - 0, 0, 0, &pSvc); + hres = + pLoc->ConnectServer(rootCimv2, nullptr, nullptr, 0, 0, 0, 0, &pSvc); if (FAILED(hres)) { spdlog::error("Failed to connect to WMI namespace. HRESULT: 0x{:x}", @@ -284,8 +289,8 @@ class HardwareInfo::Impl { * @param key Optional key to search for in the file * @return File content or value associated with key */ - auto readFile(const std::string& path, const std::string& key = "") const - -> std::string { + auto readFile(const std::string& path, + const std::string& key = "") const -> std::string { spdlog::debug("Reading file: {}", path); if (!std::filesystem::exists(path)) { diff --git a/atom/sysinfo/network/wifi/CMakeLists.txt b/atom/sysinfo/network/wifi/CMakeLists.txt index bb12510a..69adb87e 100644 --- a/atom/sysinfo/network/wifi/CMakeLists.txt +++ b/atom/sysinfo/network/wifi/CMakeLists.txt @@ -1,32 +1,26 @@ # CMakeLists.txt for atom/sysinfo/wifi module # Define the source files -set(WIFI_SOURCES - wifi.cpp - common.cpp -) +set(WIFI_SOURCES wifi.cpp common.cpp) # Add platform-specific source files if(WIN32) - list(APPEND WIFI_SOURCES windows.cpp) + list(APPEND WIFI_SOURCES windows.cpp) elseif(UNIX AND NOT APPLE) - list(APPEND WIFI_SOURCES linux.cpp) + list(APPEND WIFI_SOURCES linux.cpp) elseif(APPLE) - list(APPEND WIFI_SOURCES macos.cpp) + list(APPEND WIFI_SOURCES macos.cpp) endif() # Create the target library add_library(atom_sysinfo_wifi ${WIFI_SOURCES}) # Set include directories -target_include_directories(atom_sysinfo_wifi - PUBLIC - ${CMAKE_SOURCE_DIR} - ${CMAKE_CURRENT_SOURCE_DIR}/../../../.. - ${CMAKE_CURRENT_SOURCE_DIR}/../../network - PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR} -) +target_include_directories( + atom_sysinfo_wifi + PUBLIC ${CMAKE_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../../../.. + ${CMAKE_CURRENT_SOURCE_DIR}/../../network + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) # Set C++20 standard set_property(TARGET atom_sysinfo_wifi PROPERTY CXX_STANDARD 20) @@ -34,45 +28,31 @@ set_property(TARGET atom_sysinfo_wifi PROPERTY CXX_STANDARD_REQUIRED ON) # Add platform-specific link libraries if(WIN32) - target_link_libraries(atom_sysinfo_wifi - PUBLIC - iphlpapi - ws2_32 - wlanapi - pdh - ) + target_link_libraries(atom_sysinfo_wifi PUBLIC iphlpapi ws2_32 wlanapi pdh) elseif(UNIX AND NOT APPLE) - target_link_libraries(atom_sysinfo_wifi - PUBLIC - pthread - ) + target_link_libraries(atom_sysinfo_wifi PUBLIC pthread) elseif(APPLE) - find_library(CORE_FOUNDATION CoreFoundation REQUIRED) - find_library(SYSTEM_CONFIGURATION SystemConfiguration REQUIRED) - target_link_libraries(atom_sysinfo_wifi - PUBLIC - ${CORE_FOUNDATION} - ${SYSTEM_CONFIGURATION} - ) + find_library(CORE_FOUNDATION CoreFoundation REQUIRED) + find_library(SYSTEM_CONFIGURATION SystemConfiguration REQUIRED) + target_link_libraries(atom_sysinfo_wifi PUBLIC ${CORE_FOUNDATION} + ${SYSTEM_CONFIGURATION}) endif() # Set installation destination -install(TARGETS atom_sysinfo_wifi - LIBRARY DESTINATION lib - ARCHIVE DESTINATION lib - RUNTIME DESTINATION bin -) +install( + TARGETS atom_sysinfo_wifi + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib + RUNTIME DESTINATION bin) # Install header files -install(FILES wifi.hpp common.hpp - DESTINATION include/atom/sysinfo/wifi -) +install(FILES wifi.hpp common.hpp DESTINATION include/atom/sysinfo/wifi) # Install platform-specific headers if(WIN32) - install(FILES windows.hpp DESTINATION include/atom/sysinfo/wifi) + install(FILES windows.hpp DESTINATION include/atom/sysinfo/wifi) elseif(UNIX AND NOT APPLE) - install(FILES linux.hpp DESTINATION include/atom/sysinfo/wifi) + install(FILES linux.hpp DESTINATION include/atom/sysinfo/wifi) elseif(APPLE) - install(FILES macos.hpp DESTINATION include/atom/sysinfo/wifi) + install(FILES macos.hpp DESTINATION include/atom/sysinfo/wifi) endif() diff --git a/atom/sysinfo/network/wifi/common.cpp b/atom/sysinfo/network/wifi/common.cpp index 152b4fb4..6acc7a5c 100644 --- a/atom/sysinfo/network/wifi/common.cpp +++ b/atom/sysinfo/network/wifi/common.cpp @@ -34,7 +34,7 @@ auto getAddresses(int family, IF_ADDRS* addrs) -> int { do { *addrs = (IP_ADAPTER_ADDRESSES*)HeapAlloc(GetProcessHeap(), 0, bufLen); if (*addrs == nullptr) { - spdlog::error( "HeapAlloc failed"); + spdlog::error("HeapAlloc failed"); return -1; } @@ -50,7 +50,7 @@ auto getAddresses(int family, IF_ADDRS* addrs) -> int { iter++; } while ((rv == ERROR_BUFFER_OVERFLOW) && (iter < 3)); if (rv != NO_ERROR) { - spdlog::error( "GetAdaptersAddresses failed"); + spdlog::error("GetAdaptersAddresses failed"); return -1; } return 0; @@ -63,4 +63,4 @@ auto getAddresses(int family, IF_ADDRS* addrs) -> int { // Note: The platform-specific implementations of measurePing // are in their respective platform files -} // namespace atom::system +} // namespace atom::system diff --git a/atom/sysinfo/network/wifi/linux.cpp b/atom/sysinfo/network/wifi/linux.cpp index 60363dfb..9d21ff70 100644 --- a/atom/sysinfo/network/wifi/linux.cpp +++ b/atom/sysinfo/network/wifi/linux.cpp @@ -39,7 +39,7 @@ auto isConnectedToInternet_impl() -> bool { setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)); - struct sockaddr_in server{}; + struct sockaddr_in server {}; server.sin_family = AF_INET; server.sin_port = htons(TEST_PORT); diff --git a/atom/sysinfo/network/wifi/macos.cpp b/atom/sysinfo/network/wifi/macos.cpp index 0a8f8ec9..9978abfa 100644 --- a/atom/sysinfo/network/wifi/macos.cpp +++ b/atom/sysinfo/network/wifi/macos.cpp @@ -17,7 +17,6 @@ #include #include - namespace atom::system::macos { auto isConnectedToInternet_impl() -> bool { @@ -38,7 +37,7 @@ auto isConnectedToInternet_impl() -> bool { setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)); - struct sockaddr_in server{}; + struct sockaddr_in server {}; server.sin_family = AF_INET; server.sin_port = htons(TEST_PORT); diff --git a/atom/sysinfo/network/wifi/wifi.cpp b/atom/sysinfo/network/wifi/wifi.cpp index b04d7b0b..e23709e5 100644 --- a/atom/sysinfo/network/wifi/wifi.cpp +++ b/atom/sysinfo/network/wifi/wifi.cpp @@ -339,18 +339,21 @@ auto getNetworkSecurity() -> std::string { if (result == ERROR_SUCCESS) { for (DWORD i = 0; i < interfaceList->dwNumberOfItems; ++i) { const auto& wlanInterface = interfaceList->InterfaceInfo[i]; - if (wlanInterface.isState == wlan_interface_state_connected) { + if (wlanInterface.isState == + wlan_interface_state_connected) { WLAN_CONNECTION_ATTRIBUTES* connAttr; DWORD dataSize; result = WlanQueryInterface( handle, &wlanInterface.InterfaceGuid, wlan_intf_opcode_current_connection, nullptr, - &dataSize, reinterpret_cast(&connAttr), nullptr); + &dataSize, reinterpret_cast(&connAttr), + nullptr); if (result == ERROR_SUCCESS) { // Get security type from connection attributes - switch (connAttr->wlanSecurityAttributes.dot11AuthAlgorithm) { + switch (connAttr->wlanSecurityAttributes + .dot11AuthAlgorithm) { case DOT11_AUTH_ALGO_80211_OPEN: security = "Open (No encryption)"; break; @@ -424,7 +427,9 @@ auto getNetworkSecurity() -> std::string { // macOS: Use system_profiler to get security type std::string security = "Unknown"; - std::string cmd = "system_profiler SPAirPortDataType 2>/dev/null | grep -A 10 'Current Network' | grep 'Security'"; + std::string cmd = + "system_profiler SPAirPortDataType 2>/dev/null | grep -A 10 " + "'Current Network' | grep 'Security'"; FILE* pipe = popen(cmd.c_str(), "r"); if (pipe) { char buffer[256]; @@ -452,7 +457,8 @@ auto getNetworkSecurity() -> std::string { security = "WPA"; } else if (result.find("WEP") != std::string::npos) { security = "WEP"; - } else if (result.find("None") != std::string::npos || result.find("Open") != std::string::npos) { + } else if (result.find("None") != std::string::npos || + result.find("Open") != std::string::npos) { security = "Open (No encryption)"; } } diff --git a/atom/sysinfo/network/wifi/windows.cpp b/atom/sysinfo/network/wifi/windows.cpp index b2d7dc72..68ac76ae 100644 --- a/atom/sysinfo/network/wifi/windows.cpp +++ b/atom/sysinfo/network/wifi/windows.cpp @@ -11,7 +11,6 @@ #include #include - #undef interface namespace atom::system::windows { diff --git a/atom/sysinfo/storage/disk/disk_device.cpp b/atom/sysinfo/storage/disk/disk_device.cpp index 59529e56..cbeb53c9 100644 --- a/atom/sysinfo/storage/disk/disk_device.cpp +++ b/atom/sysinfo/storage/disk/disk_device.cpp @@ -700,7 +700,8 @@ std::optional getDeviceSerialNumber( #endif } -std::variant getDiskHealth(const std::string& /*devicePath*/) { +std::variant getDiskHealth( + const std::string& /*devicePath*/) { #ifdef _WIN32 spdlog::info("Disk health check not fully implemented for Windows"); return "Not implemented for Windows yet"; diff --git a/atom/sysinfo/storage/disk/disk_device.hpp b/atom/sysinfo/storage/disk/disk_device.hpp index 9c1813e1..fff89cb1 100644 --- a/atom/sysinfo/storage/disk/disk_device.hpp +++ b/atom/sysinfo/storage/disk/disk_device.hpp @@ -29,34 +29,39 @@ namespace atom::system { * @param includeRemovable Whether to include removable storage devices * @return A vector of StorageDevice structures */ -[[nodiscard]] auto getStorageDevices(bool includeRemovable = true) -> std::vector; +[[nodiscard]] auto getStorageDevices(bool includeRemovable = true) + -> std::vector; /** * @brief Legacy function that returns pairs of device paths and models. * @return A vector of pairs where each pair consists of device path and model. */ -[[nodiscard]] auto getStorageDeviceModels() -> std::vector>; +[[nodiscard]] auto getStorageDeviceModels() + -> std::vector>; /** * @brief Retrieves a list of all available drives on the system. * @param includeRemovable Whether to include removable drives * @return A vector of strings where each string represents an available drive. */ -[[nodiscard]] auto getAvailableDrives(bool includeRemovable = true) -> std::vector; +[[nodiscard]] auto getAvailableDrives(bool includeRemovable = true) + -> std::vector; /** * @brief Gets the serial number of a storage device * @param devicePath Path to the device * @return An optional string containing the serial number if available */ -[[nodiscard]] auto getDeviceSerialNumber(const std::string& devicePath) -> std::optional; +[[nodiscard]] auto getDeviceSerialNumber(const std::string& devicePath) + -> std::optional; /** * @brief Gets disk health information if available * @param devicePath Path to the device * @return A variant containing either a health percentage or an error message */ -[[nodiscard]] auto getDiskHealth(const std::string& devicePath) -> std::variant; +[[nodiscard]] auto getDiskHealth(const std::string& devicePath) + -> std::variant; } // namespace atom::system diff --git a/atom/sysinfo/storage/disk/disk_info.cpp b/atom/sysinfo/storage/disk/disk_info.cpp index 457184d4..9297a832 100644 --- a/atom/sysinfo/storage/disk/disk_info.cpp +++ b/atom/sysinfo/storage/disk/disk_info.cpp @@ -110,7 +110,7 @@ DiskInfo getDiskInfoCached(const std::string& path) { } #elif __linux__ - struct statfs stats{}; + struct statfs stats {}; if (statfs(path.c_str(), &stats) == 0) { info.totalSpace = static_cast(stats.f_blocks) * stats.f_bsize; info.freeSpace = static_cast(stats.f_bfree) * stats.f_bsize; @@ -151,7 +151,7 @@ DiskInfo getDiskInfoCached(const std::string& path) { } #elif __APPLE__ - struct statfs stats{}; + struct statfs stats {}; if (statfs(path.c_str(), &stats) == 0) { info.totalSpace = static_cast(stats.f_blocks) * stats.f_bsize; info.freeSpace = static_cast(stats.f_bfree) * stats.f_bsize; @@ -198,7 +198,7 @@ DiskInfo getDiskInfoCached(const std::string& path) { } #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) - struct statfs stats{}; + struct statfs stats {}; if (statfs(path.c_str(), &stats) == 0) { info.totalSpace = static_cast(stats.f_blocks) * stats.f_bsize; info.freeSpace = static_cast(stats.f_bfree) * stats.f_bsize; diff --git a/atom/sysinfo/storage/disk/disk_monitor.cpp b/atom/sysinfo/storage/disk/disk_monitor.cpp index 9ad9a021..82e4c213 100644 --- a/atom/sysinfo/storage/disk/disk_monitor.cpp +++ b/atom/sysinfo/storage/disk/disk_monitor.cpp @@ -49,20 +49,25 @@ struct MonitorContext { }; #ifdef _WIN32 -static LRESULT CALLBACK DeviceMonitorWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { +static LRESULT CALLBACK DeviceMonitorWndProc(HWND hwnd, UINT msg, WPARAM wParam, + LPARAM lParam) { MonitorContext* context = nullptr; if (msg == WM_CREATE) { const CREATESTRUCT* cs = reinterpret_cast(lParam); context = reinterpret_cast(cs->lpCreateParams); - SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast(context)); + SetWindowLongPtr(hwnd, GWLP_USERDATA, + reinterpret_cast(context)); } else { - context = reinterpret_cast(GetWindowLongPtr(hwnd, GWLP_USERDATA)); + context = reinterpret_cast( + GetWindowLongPtr(hwnd, GWLP_USERDATA)); } if (msg == WM_DEVICECHANGE && context && wParam == DBT_DEVICEARRIVAL) { - const DEV_BROADCAST_HDR* header = reinterpret_cast(lParam); + const DEV_BROADCAST_HDR* header = + reinterpret_cast(lParam); if (header->dbch_devicetype == DBT_DEVTYP_VOLUME) { - const DEV_BROADCAST_VOLUME* vol = reinterpret_cast(lParam); + const DEV_BROADCAST_VOLUME* vol = + reinterpret_cast(lParam); char driveLetter = 'A'; DWORD mask = vol->dbcv_unitmask; @@ -75,18 +80,21 @@ static LRESULT CALLBACK DeviceMonitorWndProc(HWND hwnd, UINT msg, WPARAM wParam, StorageDevice device; device.devicePath = drivePath; - device.isRemovable = (GetDriveTypeA(drivePath.c_str()) == DRIVE_REMOVABLE); + device.isRemovable = + (GetDriveTypeA(drivePath.c_str()) == DRIVE_REMOVABLE); try { if (context->securityPolicy == SecurityPolicy::READ_ONLY) { setDiskReadOnly(drivePath); - } else if (context->securityPolicy == SecurityPolicy::SCAN_BEFORE_USE) { + } else if (context->securityPolicy == + SecurityPolicy::SCAN_BEFORE_USE) { scanDiskForThreats(drivePath); } context->callback(device); } catch (const std::exception& e) { - spdlog::error("Error processing device insertion for {}: {}", drivePath, e.what()); + spdlog::error("Error processing device insertion for {}: {}", + drivePath, e.what()); } } } @@ -94,12 +102,15 @@ static LRESULT CALLBACK DeviceMonitorWndProc(HWND hwnd, UINT msg, WPARAM wParam, } #endif -std::future startDeviceMonitoring(std::function callback, - SecurityPolicy securityPolicy) { +std::future startDeviceMonitoring( + std::function callback, + SecurityPolicy securityPolicy) { g_monitoringActive = true; - return std::async(std::launch::async, [callback = std::move(callback), securityPolicy]() { - spdlog::info("Starting device monitoring with security policy: {}", static_cast(securityPolicy)); + return std::async(std::launch::async, [callback = std::move(callback), + securityPolicy]() { + spdlog::info("Starting device monitoring with security policy: {}", + static_cast(securityPolicy)); #ifdef _WIN32 WNDCLASSEXA wc{}; @@ -109,14 +120,15 @@ std::future startDeviceMonitoring(std::function